java.util.ArrayList、java.util.vector和java.util.LinkedList (JDK 1.8.0_111)

一、java.util.ArrayList

1.1 ArrayList 继承结构

ArrayList实现了RandomAccess,可以随机访问(其实就是通过数组下标访问);实现了Cloneable,可以拷贝(通过System.arraycopy方法实现);实现了Serializable,可以进行序列化,能被序列化传输。

ArrayList非线程安全。

1.2 ArrayList 属性

 1     private static final long serialVersionUID = 8683452581122892189L;
 2     // 初始化容量
 3     private static final int DEFAULT_CAPACITY = 10;
 4     // 共享空的数组
 5     private static final Object[] EMPTY_ELEMENTDATA = {};
 6     // 共享一个具有初始化容量的数组
 7     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 8 
 9     // 核心数组
10     transient Object[] elementData; // non-private to simplify nested class access
11     // 数组存放元素总数
12     private int size;

1.3 ArrayList 方法

 1     // 确保Capacity能达到指定的minCapacity
 2     public void ensureCapacity(int minCapacity) {
 3         int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
 4             // any size if not default element table
 5             ? 0
 6             // larger than default for default empty table. It's already
 7             // supposed to be at default size.
 8             : DEFAULT_CAPACITY;
 9 
10         if (minCapacity > minExpand) {
11             ensureExplicitCapacity(minCapacity);
12         }
13     }
14 
15     private void ensureCapacityInternal(int minCapacity) {
16         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
17             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
18         }
19 
20         ensureExplicitCapacity(minCapacity);
21     }
22 
23     private void ensureExplicitCapacity(int minCapacity) {
24         modCount++;
25 
26         // overflow-conscious code
27         if (minCapacity - elementData.length > 0)
28             grow(minCapacity);
29     }
30 
31     // 数组可以分配的最大长度
32     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
33 
34     // 扩容
35     private void grow(int minCapacity) {
36         // overflow-conscious code
37         int oldCapacity = elementData.length;
38         // 扩容为之前的1.5倍
39         int newCapacity = oldCapacity + (oldCapacity >> 1);
40         if (newCapacity - minCapacity < 0)
41             newCapacity = minCapacity;
42         if (newCapacity - MAX_ARRAY_SIZE > 0)
43             newCapacity = hugeCapacity(minCapacity);
44         // minCapacity is usually close to size, so this is a win:
45         // 将原数组内容拷贝到扩容后数组
46         elementData = Arrays.copyOf(elementData, newCapacity);
47     }
48 
49     private static int hugeCapacity(int minCapacity) {
50         if (minCapacity < 0) // overflow
51             throw new OutOfMemoryError();
52         return (minCapacity > MAX_ARRAY_SIZE) ?
53             Integer.MAX_VALUE :
54             MAX_ARRAY_SIZE;
55     }

上面是ensureCapacity方法,确保Capacity能大于等于minCapacity。ArrayList每次扩容为之前的1.5倍。

 1     public Object clone() {
 2         try {
 3             ArrayList<?> v = (ArrayList<?>) super.clone();
 4             v.elementData = Arrays.copyOf(elementData, size);
 5             v.modCount = 0;
 6             return v;
 7         } catch (CloneNotSupportedException e) {
 8             // this shouldn't happen, since we are Cloneable
 9             throw new InternalError(e);
10         }
11     }

拷贝ArrayList方法,这也不是一种深度拷贝,只是将源数组的内容拷贝到新数组,元素并未重建(拷贝)。

 1     public void add(int index, E element) {
 2         rangeCheckForAdd(index);
 3         // 确保容量足够
 4         ensureCapacityInternal(size + 1);  // Increments modCount !!
 5         // 将index及其后元素统统向后移动一位 
 6         System.arraycopy(elementData, index, elementData, index + 1,
 7                          size - index);
 8         elementData[index] = element;
 9         size++;
10     }
11 
12     public E remove(int index) {
13         rangeCheck(index);
14 
15         modCount++;
16         E oldValue = elementData(index);
17 
18         int numMoved = size - index - 1;
19         if (numMoved > 0)
20             // 删除的不是最后的一个元素,需要将后面的元素向前移动一个位置
21             System.arraycopy(elementData, index+1, elementData, index,
22                              numMoved);
23         elementData[--size] = null; // clear to let GC do its work
24 
25         return oldValue;
26     }

ArrayList其根本是个数组,因此在数组末尾增删元素是很高效的。但是要在中间增删元素,就必须移动后面的所有元素,效率肯定会有影响。

如果需要频繁向中间增删元素还是LinkedList比较合适。

 1     public boolean remove(Object o) {
 2         if (o == null) {
 3             for (int index = 0; index < size; index++)
 4                 if (elementData[index] == null) {
 5                     fastRemove(index);
 6                     return true;
 7                 }
 8         } else {
 9             for (int index = 0; index < size; index++)
10                 if (o.equals(elementData[index])) {
11                     fastRemove(index);
12                     return true;
13                 }
14         }
15         return false;
16     }

remove某个value,找到第一个value之后,就删除并return。

    // 将ArrayList写入到ObjectOutputStream
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        // 将非静态和非transient字段写入流
        s.defaultWriteObject();

        // 将size写入流
        s.writeInt(size);

        // 将所有的元素写入流中
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // 从流中读入非静态和非transient字段
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // 读入size
        s.readInt(); // ignored

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            // 读取元素
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

这两个方法将ArrayList写入流或者从流中读入。但是我有个疑问,都是private方法,这个写出来谁能调用? 

二、java.util.Vector

2.1 Vector 继承结构

Vector继承结构和ArrayList相同。ArrayList是非线程安全的,与之对应的Vector是线程安全的。

2.2 Vector 属性

1     // 存储对象的数组
2     protected Object[] elementData;
3     // 可以理解为Vector的capacity,也可以理解为数组的length
4     protected int elementCount;
5     // 可以自定义capacity扩容量
6     protected int capacityIncrement;
7     private static final long serialVersionUID = -2767605614048989439L;

2.3 Vector 方法

扩容方法

 1     public synchronized void ensureCapacity(int minCapacity) {
 2         if (minCapacity > 0) {
 3             // 注意这里会导致modCount++ !!
 4             modCount++;   
 5             ensureCapacityHelper(minCapacity);
 6         }
 7     }
 8 
 9     private void ensureCapacityHelper(int minCapacity) {
10         // overflow-conscious code
11         if (minCapacity - elementData.length > 0)
12             // 需要扩容处理
13             grow(minCapacity);
14     }
15 
16     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
17 
18     private void grow(int minCapacity) {
19         // overflow-conscious code
20         int oldCapacity = elementData.length;
21         // 如果没有自定义扩容增量capacityIncrement,则直接扩容为之前的容量的两倍
22         int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
23                                          capacityIncrement : oldCapacity);
24         if (newCapacity - minCapacity < 0)
25             // 扩容为之前容量的两倍如果还不够,则直接扩容为minCapacity
26             newCapacity = minCapacity;
27         if (newCapacity - MAX_ARRAY_SIZE > 0)
28             // 防止扩容超出设定的范围
29             newCapacity = hugeCapacity(minCapacity);
30         // 将元素拷贝到新数组
31         elementData = Arrays.copyOf(elementData, newCapacity);
32     }
33 
34     private static int hugeCapacity(int minCapacity) {
35         if (minCapacity < 0) // overflow
36             throw new OutOfMemoryError();
37         return (minCapacity > MAX_ARRAY_SIZE) ?
38             Integer.MAX_VALUE :
39             MAX_ARRAY_SIZE;
40     }

ensureCapacity方法加了synchronized同步锁。在扩容策略上跟ArrayList稍有不同,ArrayList默认扩容为之前容量的1.5倍,而Vector则默认扩容为之前容量的2倍。除此之外Vector还允许自定义扩容增量。

1     public synchronized List<E> subList(int fromIndex, int toIndex) {
2         return Collections.synchronizedList(super.subList(fromIndex, toIndex),
3                                             this);
4     }

在subList方法中,为了返回一个线程安全的List,加上了Collections.synchronizedList封装。

If you need synchronization, a Vector will be slightly faster than an ArrayList synchronized with Collections.synchronizedList.

Collections.synchronizedList封装是对List对象添加同步锁,各方法本质上还是调用的List的方法。

Vector类其他方法和ArrayList差不多,无非加上了一个synchronized同步处理,这里就不再赘述了。 

三、java.util.LinkedList

3.1 LinkedList继承结构

LinkedList没有实现RandomAccess interface,可见其并不具备随机访问特性,毕竟是链表。

LinkedList同样是非线程安全的。

LinkedList比较适合从中间删除、添加元素的场景;ArrayList则更适合只在末尾增删元素,遍历的场景。

3.2 LinkedList 属性

1     transient int size = 0;
2     // 指向第一个节点
3     transient Node<E> first;
4     // 指向最后一个节点
5     transient Node<E> last;

其中Node类为:

 1     private static class Node<E> {
 2         E item;
 3         Node<E> next;
 4         Node<E> prev;
 5 
 6         Node(Node<E> prev, E element, Node<E> next) {
 7             this.item = element;
 8             this.next = next;
 9             this.prev = prev;
10         }
11     }

Node节点包含next和prev两个指针,分别指向前面和后面的一个节点。

3.3 LinkedList 方法

 1     public E set(int index, E element) {
 2         // 检查index是否在范围内
 3         checkElementIndex(index);
 4         // 找出这个node节点
 5         Node<E> x = node(index);
 6         // 保存旧的值
 7         E oldVal = x.item;
 8         // 更新值
 9         x.item = element;
10         return oldVal;
11     }
12 
13     Node<E> node(int index) {
14         // assert isElementIndex(index);
15         // 如果index指向链表前半部分,则从前向后遍历
16         if (index < (size >> 1)) {
17             Node<E> x = first;
18             for (int i = 0; i < index; i++)
19                 x = x.next;
20             return x;
21         } else {
22             // index指向链表的后半部分,从后往前遍历
23             Node<E> x = last;
24             for (int i = size - 1; i > index; i--)
25                 x = x.prev;
26             return x;
27         }
28     }
29 
30     private void checkElementIndex(int index) {
31         if (!isElementIndex(index))
32             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
33     }
34 
35     private boolean isElementIndex(int index) {
36         return index >= 0 && index < size;
37     }

在这个set方法中,有个参数是index,修改指定index上的元素内容,但链表本身是不具备index属性的,为此通过node()方法遍历(从first向last方向开始计数)找到第index个Node,即为需要修改的节点。

node方法为了提高效率,根据index所指向的位置,从前向后或从后向前遍历,因此最多遍历size/2个节点就可以找到index节点了。

 1     // 获取第一个节点内容,但是不删除
 2     public E peek() {
 3         final Node<E> f = first;
 4         return (f == null) ? null : f.item;
 5     }
 6 
 7     // 获取第一个节点内容并且删除之
 8     public E poll() {
 9         final Node<E> f = first;
10         return (f == null) ? null : unlinkFirst(f);
11     }
12 
13     // 删除第一个节点并返回其内容,跟poll一样
14     public E pop() {
15         return removeFirst();
16     }
17 
18     // 添加一个元素到结尾
19     public boolean offer(E e) {
20         return add(e);
21     }
22 
23     // 添加一个元素到开头
24     public void push(E e) {
25         addFirst(e);
26     }

上面是几个比较容易混淆的方法,其实看下代码就明白了。

 1     private class DescendingIterator implements Iterator<E> {
 2         private final ListItr itr = new ListItr(size());
 3         public boolean hasNext() {
 4             return itr.hasPrevious();
 5         }
 6         public E next() {
 7             return itr.previous();
 8         }
 9         public void remove() {
10             itr.remove();
11         }
12     }

LinkedList提供了一个反向迭代器,因为LinkedList本身就是一个双向链表,反向遍历也是很方便的。

 1     private LinkedList<E> superClone() {
 2         try {
 3             return (LinkedList<E>) super.clone();
 4         } catch (CloneNotSupportedException e) {
 5             throw new InternalError(e);
 6         }
 7     }
 8 
 9     public Object clone() {
10         LinkedList<E> clone = superClone();
11 
12         // Put clone into "virgin" state
13         clone.first = clone.last = null;
14         clone.size = 0;
15         clone.modCount = 0;
16 
17         // Initialize clone with our elements
18         for (Node<E> x = first; x != null; x = x.next)
19             clone.add(x.item);
20 
21         return clone;
22     }

拷贝方法,同样是浅拷贝,每个节点的元素内容并没有被拷贝。拷贝出来的结果是两个链表都引用的同一个元素内容。

原文地址:https://www.cnblogs.com/snowater/p/8057216.html