Java ArrayList源码学习

ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable

一 自动扩容机制

Resizable-array implementation of the List interface.
capacity: 存储list元素的底层数组的大小;
list size:存储的list元素的个数;

  1. 初始化
    不指明容量时,底层数组默认是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即{}
    指明容量为0时,底层数组为EMPTY_ELEMENTDATA,即{}
    指明容量>0时, this.elementData = new Object[initialCapacity];

    public ArrayList(int initialCapacity) {
           if (initialCapacity > 0) {
               this.elementData = new Object[initialCapacity];
           } else if (initialCapacity == 0) {
               this.elementData = EMPTY_ELEMENTDATA;
           } else {
               throw new IllegalArgumentException("Illegal Capacity: "+
                                                  initialCapacity);
           }
       }
     // Constructs an empty list with an initial capacity of ten.
    public ArrayList() { 
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
  2. add

        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // 确保底层数组的容量
            elementData[size++] = e;
            return true;
        }
    

    先计算需要的容量,然后进行分配;

        private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    

    计算所需的最小容量
    如果开始没有指定容量大小,最低容量设为Math.max(10, minCapacity);
    为什么没有指定容量时,不直接设为10呢?反序列化时也会调用,minCapacity可能>10;
    如果开始制定了容量为0,最低容量是0;

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
    }
    

    底层数组容量是否改变,均执行modCount++; 表示list结构性变化;

    private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    

    实际的数组扩增函数:1.5倍扩增

        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
                                       //实际上小于MAX_ARRAY_SIZE,就报错OutOfMemoryError
        /**
         * Increases the capacity to ensure that it can hold at least the
         * number of elements specified by the minimum capacity argument.
         *
         * @param minCapacity the desired minimum capacity
         */
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    ArrayList list =new ArrayList();
    list.add(1); 默认数组分配容量为10

  3. remove
    remove函数并不会调节底层数组的大小;

        public E remove(int index) {
            rangeCheck(index);   //范围检查
    
            modCount++;  //结构性改变
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }
    

    clear函数也并不会调节底层数组的大小;

        /**
         * Removes all of the elements from this list.  The list will
         * be empty after this call returns.
         */
        public void clear() {
            modCount++;
    
            // clear to let GC do its work
            for (int i = 0; i < size; i++)
                elementData[i] = null;
    
            size = 0;
        }
    

    真正减少底层数组容量的函数调用 trimToSize()

        /**
         * Trims the capacity of this ArrayList instance to be the
         * list's current size.  An application can use this operation to minimize
         * the storage of an ArrayList instance.
         */
        public void trimToSize() {
            modCount++;
            if (size < elementData.length) {
                elementData = (size == 0)
                  ? EMPTY_ELEMENTDATA
                  : Arrays.copyOf(elementData, size);
            }
        }
    

二: 线程不安全

This class is roughly equivalent to Vector, except that it is unsynchronized.
推荐方式:List list = Collections.synchronizedList(new ArrayList(...));


三: Fail-Fast 机制,ConcurrentModificationException

The iterators returned by this class's iterator methods are fail-fast:
if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own ListIterator.remove() methods, the iterator will throw a ConcurrentModificationException

  1. A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification. (内部int值modCount负责记录更改次数)

  2. 一旦创建了Iterator后,Iterator会记录当前list的modCount,并保存为expectedModCount;
    此后外部list发生结构性更改,外部modCount更改,而内部expectedModCount不变,再次调用Iterator函数时无法通过检查,报错;

    private class Itr implements Iterator {
            int cursor;       // index of next element to return
            int lastRet = -1; // index of last element returned; -1 if no such
            int expectedModCount = modCount;
    
            public boolean hasNext() {
                return cursor != size;
            }
    
            @SuppressWarnings("unchecked")
            public E next() {
                checkForComodification();
                int i = cursor;
                if (i >= size)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;
                return (E) elementData[lastRet = i];
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    

    Iterator.remove是删除前一次返回的值;
    Iterator不能连续remove的原因,remove后 lastRet = -1;

  3. 解决方式:
    3.1)Iterator创建之后,不能用ArrayList增删元素。 只能用Iterator删除元素;
    因为Iterator.remove会同步更新list的modCount以及expectedModCount;
    subList 和Iterator 一样,创建后list就不能结构性修改了;list的增删不要Iterator的操作混在一起
    3.2)java1.8后,貌似removeIf函数式编程也可以避免,待研究;
    注意
    ArrayList线程不安全,改为Vector或者Collections.synchronizedList(list);并不能避免ConcurrentModificationException。
    因为线程安全是多线程排队访问list,不同时访问。仍然允许一个创建了Iterator,另一个修改,只要不是同时就没问题。


四: 总结

  1. ArrayList默认扩增方式为1.5倍;
  2. ArrayList随机访问get,set是0(1)时间开销,add有可能扩容,移动数组元素0(n)时间开销;删除移动数组元素0(n)时间开销;Iterator.remove移动数组元素0(n)时间开销;
    数组优势,数组劣势
  3. list转array
      List v = new ArrayList<>();
      return v.toArray(new String[v.size()]);
    

@梦工厂 2018.3.6

你可能感兴趣的:(Java ArrayList源码学习)