zoukankan      html  css  js  c++  java
  • 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     }

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

  • 相关阅读:
    修改linux的ssh默认端口号22的方法
    centos6 配置静态IP
    homebrew 无法安装提示不能在根目录下使用
    MySQL Replication 详解MySQL数据库设置主从同步的方法
    两台Mysql数据库数据同步实现
    MySql主从同步和延迟同步
    c#委托和事件
    C# Out和Ref区别
    MVC与三层架构
    经典数据库题
  • 原文地址:https://www.cnblogs.com/snowater/p/8057216.html
Copyright © 2011-2022 走看看