集合(二)List,ArrayList,LinekList,Vector

ArrayList,LinkedList,Vector

List,Queue中的元素有序可重复

ArrayList,LinekList,Vector 均为可伸缩数组,既可以动态改变长度的数组。

1.ArrayList

数组实现,查询速度快,插入慢,非线程安全。ArrayList底层维护了一个Object[]用于存储对象,默认数组的长度是10。可以通过new ArrayList(20)显式的指定用于存储对象的数组的长度。

它继承于AbstractList,实现了List, RandomAccess(标记接口,无任何方法。快速随机访问存储元素), Cloneable(克隆), Serializable(序列化,hessian协议传输)接口。

1.添加元素时,ensureCapacityInternal()方法会计算ArrayList的扩容大小。如果需要扩容,扩容的大小为原来的1.5倍,实际上是使用Arrays.copyOf()方法实现的。

2.删除元素时,如果不是最后一个元素,最终都会调用System.arraycopy()方法进行数组复制操作

3.modeCount变量的含义:在迭代器初始化时,将modeCount的值赋值给expectedModCount,每一次增删改,modCount的值都会加1,因此modeCount记录了ArrayList内发生改变的次数。

迭代获取下一个元素的时候,会判断expectedModCount的值与modeCount是否一致,如果不一致,则会报异常

4.transient变量:当序列化对象的时候,如果对象的某个属性不进行序列化操作,则用transient修饰就可以。

5.elementData是ArrayList集合中保存元素的数组。ArrayList中对于elementData变量就用transient来修饰,为什么这个变量不序列化呢?

ArrayList在添加元素时,可能会对elementData数组进行扩容操作,而扩容后的数组可能并没有全部保存元素。

例如:我们创建了new Object[10]数组对象,但是我们只向其中添加了1个元素,而剩余的9个位置并没有添加元素。当我们进行序列化时,并不会只序列化其中一个元素,而是将整个数组进行序列化操作,那些没有被元素填充的位置也进行了序列化操作,间接的浪费了磁盘的空间,以及程序的性能。所以,ArrayList才会在elementData属性前加上transient修饰符。

ArrayList在序列化时会调用writeObject(),直接将elementData写入ObjectOutputStream;

而反序列化时则调用readObject(),从ObjectInputStream获取elementData;

Arrays.copyOf()方法的底层调用System.arrayCopy()方法。arrayCopy()方法使用了native关键字修饰,调用的是c++的底层函数

2.Vector

队列实现,并且实现了RandomAccess接口,可以随机访问,绝大多数方法都是直接或者间接同步的,是线程安全的ArrayList

Vector和ArrayList都会在内存中开辟一块连续的空间来存储,数据存储是连续的.

3.LinkedList

jdk1.7后, LinkedList变成了直线型链表结构,双向循环链表实现的,插入快,查找慢,非线程安全。查找的时候从头到尾查找,在内存中的地址不一定连续。上一个元素需要记住下一个元素。descendingIterator()函数可以逆序迭代,因此LinkedList可以模拟队列(list.offer("**")先进先出list.poll())或者堆栈(list.push("***")后进先出list.pop())数据结构

它继承AbstractSequentialList,实现了List, Deque, Cloneable, Serializable接口。

Deque是一个双向队列,既可以先入先出,也可以先入后出(既可以在头部添加元素,也可以在尾部添加元素)

node(int index)方法至关重要,通过对应角标获取到对应的集合元素。node()中是根据角标的大小是选择从前遍历还是从后遍历整个集合。也可以间接的说明,LinkedList在随机获取元素时性能很低,每次的获取都得从头或者从尾遍历半个集合。

node方法:

//设置对应角标的元素:
public E set(int index, E element) {
    checkElementIndex(index);
    //通过node()方法,获取到对应角标的元素:
    java.util.LinkedList.Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
//获取对应角标所属于的结点:
java.util.LinkedList.Node<E> node(int index) {
    //位运算:如果位置索引小于列表长度的一半,则从头开始遍历;否则,从后开始遍历;
    if (index < (size >> 1)) {
        java.util.LinkedList.Node<E> x = first;
        //从头结点开始遍历:遍历的长度就是index的长度,获取对应的index的元素
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        //从集合尾结点遍历:
        java.util.LinkedList.Node<E> x = last;
        //同样道理:
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

get()方法:核心还是调用node()方法

参考:https://blog.csdn.net/qq_33642117/article/details/51998866

https://mp.weixin.qq.com/s?__biz=MzI5ODI5NDkxMw==&mid=2247486782&idx=1&sn=67ad4a2e45dfcefc6df5e77d8a94e572&chksm=eca946d0dbdecfc6ee491c600e8899da6f05141ba839f49bae450f08f7118c9a5b8637a55c19&scene=21#wechat_redirect

猜你喜欢

转载自blog.csdn.net/weixin_38108266/article/details/81877815