ArrayList源码解析(JDK1.8)

文档头

  1. Arraylist 是List接口实现类,是可扩容数组。允许空值,粗略地认为与Vector相同,不过是非同步的。
  2. capacity指定内部数组容量,可以自动增长。存入大量数据前可以ensureCapacity(),确保容量达到一定值,也可以称作扩容
  3. 非同步地, 多线程访问发生结构性修改时,必须外部同步操作;或者用Collections.synchronizedList。
  4. iterator()是fast-fail型的。 iterator生成后,除了iterator自己的add()或remove()方法,其它对实例的结构性修改都会报ConcurrentModificationException。

变量定义

  •     private static final int DEFAULT_CAPACITY = 10; // 默认容量 10
  •     private static final Object[] EMPTY_ELEMENTDATA = {}; // 空数组
  •     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认容量的空数组
  •     transient Object[] elementData; // 存储数据的数组
  •     private int size; //  数据个数

构造方法

  • public ArrayList() {
        // 空参构造数组为{}
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

     

  •    public ArrayList(int initialCapacity) {
            //initialCapacity > 0 时,创建长度为initialCapacity的数组;
            if (initialCapacity > 0) { 
                this.elementData = new Object[initialCapacity];
            //  initialCapacity  == 0 , 取{};
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                // initialCapacity < 0 时报错。
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
    
     
  • public ArrayList(Collection<? extends E> c) {
        // 将collection转换成数组赋给eleData
        elementData = c.toArray();
        // 计算size, 判断size != 0, 
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else { // size == 0时,取{}.
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

     

重要方法

  1. ensureCapacity(int minCapacity)  扩容 在即将数据前先将数组扩容,可以避免数组频繁自动扩容影响效率。
        public void ensureCapacity(int minCapacity) {
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                ? 0
                : DEFAULT_CAPACITY;
            if (minCapacity > minExpand) { // minCapacity 大于默认容量(10)时开始扩容
                ensureExplicitCapacity(minCapacity);
            }
        }
    
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++; // 增加modCount
            // overflow-conscious code
            if (minCapacity - elementData.length > 0) // 新容量 > 当前容量,开始扩容
                grow(minCapacity);
        }
    
       private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length; 
            int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量初始化为(旧容量 * 1.5)
            if (newCapacity - minCapacity < 0) // 新容量 < 指定的minCapacity时,新容量定为                
            // minCapacity。所以扩容后的大小为 minCapacity与(oldCapacity*1.5)较大值
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            // 创建长度为newCapacity的新数组, 将数据复制到新数组。
            elementData = Arrays.copyOf(elementData, newCapacity); 
        }

     Arrays.copyOf方法会创建容量为newCapacity的新数组,并将eleData的数据复制过来; 扩容的容量是 minCapacity与(oldCapacity * 1.5)较大值。

  2. add(E e)
       public boolean add(E e) {
            // 扩容, 然后在数组末尾添加元素
            ensureCapacityInternal(size + 1);  
            elementData[size++] = e;
            return true;
        }

     

  3. add(int index, E element) 
     public void add(int index, E element) {
            // 判断index是否越界
            rangeCheckForAdd(index);
            // 扩容
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            // 将 index开始的元素,全部复制到index+1后,集体移动。  
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            // 赋值 index位置。
            elementData[index] = element;
            size++;
        }

     

  4. remove(int index)  

        public E remove(int index) {
            rangeCheck(index); // 判断
            modCount++;
            E oldValue = elementData(index); // 获取原对象
            int numMoved = size - index - 1;
            if (numMoved > 0) // 将index+1的一起移动到index,集体移动
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // 将原先的末尾元素置null。
            return oldValue;
        }

     

  5. remove(Object o) 

     public boolean remove(Object o) {
            if (o == null) { // o为null时, 寻找第一个null的偏移量,调用fastRemove()删除
                for (int index = 0; index < size; index++)
                    if (elementData[index] == null) {
                        fastRemove(index);
                        return true;
                    }
            } else {
                for (int index = 0; index < size; index++)
        // 寻找第一个的偏移量,调用fastRemove()删除
                    if (o.equals(elementData[index])) { 
                        fastRemove(index);
                        return true;
                    }
            }
            return false;
        }
        private void fastRemove(int index) {
            modCount++;
            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
        }

     

  6. addAll(Collection<? extends E> c) 
       public boolean addAll(Collection<? extends E> c) {
            // 转换为数组
            Object[] a = c.toArray();
            int numNew = a.length;
            // 扩容为size + c.length
            ensureCapacityInternal(size + numNew);  // Increments modCount
            // 将a元素从0开始复制到 eleData,从size开始。
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
            return numNew != 0;
        }

    ArrayList中频繁使用System.arraycopy对元素进行集体移位,removeAll操作与这个类似。

  7. toArray()  

      public Object[] toArray() {
            // 将elementData元素复制进一个新数组
            return Arrays.copyOf(elementData, size);
        }
     public <T> T[] toArray(T[] a) {
            if (a.length < size)
                // a.length < size时, 将部分元素存储进新数组
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());
            // a.length >= size 时, 将eleData数据复制进a, 不用创建新数组。
            System.arraycopy(elementData, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

    第一种方式总会创建“安全”的新数组,并复制元素到新数组;第二张方式当 a.length < size时会创建新数组, 否则直接存在a中。

Iterator 

 private class Itr implements Iterator<E> {
        int cursor;       // next()方法返回元素的偏移量
        int lastRet = -1; // 上一个返回元素的下标,也是remove()方法删除的位置
        int expectedModCount = modCount; // modCount的期望值, 用作fast-fail

        Itr() {}

        public boolean hasNext() { // 
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();  // 判断modCount, 不匹配抛出异常
            int i = cursor;
            if (i >= size)    // 是否越界
                throw new NoSuchElementException(); 
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)  
                throw new ConcurrentModificationException();
            cursor = i + 1; // cursor指向下一个元素位置
            return (E) elementData[lastRet = i]; // 返回元素, 并将返回元素位置记录
        }

        public void remove() {
            if (lastRet < 0)  // 上次没有返回元素, 报错
                throw new IllegalStateException();
            checkForComodification(); // 判断modCount
            try {
                ArrayList.this.remove(lastRet); // 调用外部remove()方法, 删除上一个返回            
            // 位置的元素
                cursor = lastRet; // 指向后一个
                lastRet = -1; 
                expectedModCount = modCount; // 重新记录expectedModCount
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

可以看到Iterator创建时会存储当前时刻的modcount,当执行next()、remove()时,会检测当前的modeCount和内部存储的expectedModCount相等,不相等时直接抛出ConcurrentModificationException。多线程同时访问ArrayLis并可能发生修改时t时,必须在外部同步或使用同步Wrapper。

Iterator执行next()时会返回eleData[cursor]处元素,将cursor记录于lastret,以便删除返回对象;然后cursor值加1,以便获取下一个对象 。。

 在Iterator内部成功调用remove()方法会调用外部的remove(int index)方法,并重新赋值expectedModCount。

ListIterator

ListIterator接口继承了Iterator接口,提供更丰富的功能。可以双向遍历,也可以在遍历过程中插入元素、修改元素。 

* Marker interface used by <tt>List</tt> implementations to indicate that
 * they support fast (generally constant time) random access.
  private class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            super();
            cursor = index;
        }

        // 省略了很多。。。。。。。。。。
    }

 

总结

  1. ArrayList由数组实现,添加、删除操作都是对数组元素的复制操作实现
  2. 扩容时新容量,大于等于原容量的1.5倍,当指定的新容量大于原容量1.5倍时,取指定容量;当添加元素时会ensureCapacity,容量不足时会自动扩容。 因此在添加大量元素前,应该手动指定容量,以免多次发生自动扩容。
  3. toArray()会分配新数组并返回Object[];toArray(T[] a)当 a.length < size时,创建新数组,并复制eleData的一部分,,否则直接将eleData数据存入a
  4. Iterator()方法返回Itr类实例,记录当前cursor、lastRet、expectModCount。cursor用于next()获取下一个元素;lastRet用于remove()删除上一次返回的元素,过程中会调用外部remove()方法并重新记录modCount;modCount在初始化Iterator时或remove()时被赋值,其余时候用来判定是否发生并发修改,有就抛出异常。
  5. ListIterator()提供更丰富的功能

其它

偶然发现 ArrayList实现了一个RandomAccess接口,

* Marker interface used by <tt>List</tt> implementations to indicate that
* they support fast (generally constant time) random access.
 * for typical instances of the class, this loop:
 * <pre>
 *     for (int i=0, n=list.size(); i &lt; n; i++)
 *         list.get(i);
 * </pre>
 * runs faster than this loop:
 * <pre>
 *     for (Iterator i=list.iterator(); i.hasNext(); )
 *         i.next();
 * </pre>

RandomAccess是一个标记接口,表示一个类支持快速随机访问。  在遍历过程中用for (int i=0, n=list.size(); i++)形式会比用迭代器快。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值