ArrayList

作者: navyd | 来源:发表于2019-02-06 13:31 被阅读0次

    ArrayList

    List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。

    size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。

    类结构:

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    

    主要字段

        /**
         * Default initial capacity.
         */
        private static final int DEFAULT_CAPACITY = 10;
        /**
         * Shared empty array instance used for empty instances.
         */
        // 用户显式指定list为空时使用的数组
        private static final Object[] EMPTY_ELEMENTDATA = {};
        /**
         * Shared empty array instance used for default sized empty instances. We
         * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
         * first element is added.
         */
        // 当使用默认无参构造器创建的空list数组,在扩容时会考虑使用默认的扩容方案DEFAULT_CAPACITY
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        /**
         * The array buffer into which the elements of the ArrayList are stored.
         * The capacity of the ArrayList is the length of this array buffer. Any
         * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         * will be expanded to DEFAULT_CAPACITY when the first element is added.
         */
        transient Object[] elementData; // non-private to simplify nested class access
        /**
         * The size of the ArrayList (the number of elements it contains).
         * @serial
         */
        private int size;
    

    构造器

    ArrayList所有的构造器:

    arraylist_1.png

    ArrayList提供了3个构造器,除了Collection集合标准中提供无参和Collection参数的两个构造器外,额外提供int参数用于数组容量的初始化

        /**
        构造一个空列表。默认的初始容量grow时为10并不是在初始时就创建,而是在需要空间时初始化
        */
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
        /**
        构造一个使用指定 collection 并按其元素迭代的顺序排列的列表。 
        */
        public ArrayList(Collection<? extends E> c) {
            // 集合c元素的object[]数组(不能确保一定为实际类型为object类型)
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)  // 如果实际类型不是Object就复制到新Object数组中
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } 
          // 传入是一个空的collection
          else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
        /**
        构造一个具有指定初始容量并立即初始化分配空间的空列表。 
        */
        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);
            }
        }
    

    注意

    • 默认无参构造器会使用一个默认空的元素数组:DEFAULTCAPACITY_EMPTY_ELEMENTDATA,用于区别用户显式指定(通过另外两个构造器)为空的元素数组:EMPTY_ELEMENTDATA。

    在没有指定扩容容量时,区别在于首次自动扩容时前者会默认扩容为DEFAULT_CAPACITY=10,后者只会size+1即扩容容量为1

    • 对于参数为Collection的构造器应该少用Arrays.asList()将数组转化为ArrayList。

    由于Arrays.asList().toArray()的方法不能将原数组转化为Object[],构造器会再次将数组复制为Object[],造成Arrays.asList().toArray()这一个额外的开销(内部实现为arr.clone()),如果数组过大将造成明显的性能损失

    • 创建ArrayList时应该显式指定list的容量,不应该指定容量为0

    在add首次扩容时,指定容量为0的EMPTY_ELEMENTDATA的扩容为1,初始数组容量过小,造成频繁的扩容操作

    扩容

    • void ensureCapacity(int minCapacity)
    /**
         * Increases the capacity of this <tt>ArrayList</tt> instance, if
         * necessary, to ensure that it can hold at least the number of elements
         * specified by the minimum capacity argument.
         *
         * @param   minCapacity   the desired minimum capacity
         */
        // 如果指定的容量比默认的方案大,就设置为指定容量扩容。
        public void ensureCapacity(int minCapacity) {
            // 默认空的list实例最小扩容为10,否则就为0。elementData设置为非DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示用户指定了容量或collection构造arrayList
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                // any size if not default element table
                ? 0
                // larger than default for default empty table. It's already
                // supposed to be at default size.
                : DEFAULT_CAPACITY;
    
            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity);
            }
        }
    

    扩容的私有方法实现:

        // 在指定容量和默认容量间选择更大的扩容容量
        private void ensureCapacityInternal(int minCapacity) {
            // 如果是默认大小的list实例,最小容量应该比默认容量10要大,否则使用默认容量
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            ensureExplicitCapacity(minCapacity);
        }
    
        /**
         * 如果指定的容量大于数组长度elementData.length就调用grow()扩大数组
         * @param minCapacity
         */
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            // 如果期望容量大于当前数组容量就扩大数组
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
        /**
         * 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
         */
        // 扩大数组容量为1.5倍旧容量或更大的指定容量
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            // 新容量为旧容量的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            // 如果指定容量大于1.5倍旧容量就取指定容量
            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);
        }
        // 如果指定容量超过2^31就抛出异常,否则将容量设置为Integer.MAX_VALUE
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    

    注意

    • 通过无参构造器设置默认扩容为DEFAULT_CAPACITY=10,其余构造器默认扩容为0,对于默认构造的list实例,每次扩容都需要将指定容量与默认容量比较,选择较大的扩容
    • 如果指定的扩容容量小于当前数组长度,将不会发生实质的扩容操作
    • 每次实际扩容的容量至少在1.5倍旧容量或更大的指定容量,不会造成频繁的扩容操作

    查询

        public int size() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        // 具体indexOf()看后面
        public boolean contains(Object o) {
            return indexOf(o) >= 0;
        } 
    

    搜索

    • int indexOf(Object o)
        /**
         * Returns the index of the first occurrence of the specified element
         * in this list, or -1 if this list does not contain the element.
         * More formally, returns the lowest index <tt>i</tt> such that
         * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
         * or -1 if there is no such index.
         */
        // 覆盖AbstractList的实现,通过数组下标随机访问加快速度,而不是通过迭代器访问
        public int indexOf(Object o) {
            // 如果指定参数为null,就在数组中搜索为null的元素
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } 
            // 否则就在数组中寻找与指定参数相等的元素
            else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
    
    • int lastIndexOf(Object o)
        /**
         * Returns the index of the last occurrence of the specified element
         * in this list, or -1 if this list does not contain the element.
         * More formally, returns the highest index <tt>i</tt> such that
         * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
         * or -1 if there is no such index.
         */
        public int lastIndexOf(Object o) {
            if (o == null) {
                // 从数组最后往前遍历
                for (int i = size-1; i >= 0; i--)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = size-1; i >= 0; i--)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
    

    转换

    • Object[] toArray()
        /**
         * Returns an array containing all of the elements in this list
         * in proper sequence (from first to last element).
         */
        // 复制ArrayList底层数组为一个Object[]
        public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
    
    • T[] toArray(T[] a)
        /**
         * Returns an array containing all of the elements in this list in proper
         * sequence (from first to last element); the runtime type of the returned
         * array is that of the specified array.  If the list fits in the
         * specified array, it is returned therein.  Otherwise, a new array is
         * allocated with the runtime type of the specified array and the size of
         * this list.
         *
         * <p>If the list fits in the specified array with room to spare
         * (i.e., the array has more elements than the list), the element in
         * the array immediately following the end of the collection is set to
         * <tt>null</tt>.  (This is useful in determining the length of the
         * list <i>only</i> if the caller knows that the list does not contain
         * any null elements.)
         */
        // 将集合转换为指定数组类型。如果指定数组长度小于集合大小,就填充到新数组,如果大于就在数组的集合元素最后位置(collection.size())置为null
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            if (a.length < size)
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());
            System.arraycopy(elementData, 0, a, 0, size);
            // 如果指定的数组长度大于list的数组,只将指定数组的list.size()处置为null
            if (a.length > size)
                a[size] = null;
            return a;
        }
    

    注意

    • T[] toArray(T[] a)的参数数组长度应该设置为a.length = collection.size()

    如果设置length过小,Arrays.copyOf会通过反射重新创建指定类型的空数组并将元素复制过去,会造成反射和创建对象的开销。

    • T[] toArray(T[] a)对于过大的指定数组长度时会将a[list.size()]位置置为null,但其后续元素仍然存在,使用数组时需要注意

    添加

    • boolean add(E e)
        /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return <tt>true</tt> (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
            // 只有当size+1 > 数组.length时才会执行扩容,每次扩容至少在1.5倍旧容量之上
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        } `
    
    • void add(int index, E element)
        /**
         * Inserts the specified element at the specified position in this
         * list. Shifts the element currently at that position (if any) and
         * any subsequent elements to the right (adds one to their indices).
         *
         * @param index index at which the specified element is to be inserted
         * @param element element to be inserted
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public void add(int index, E element) {
            // 检查index下界和上界,检查下界是防止System.arraycopy抛出异常IndexOutOfBoundsException
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
    
    • boolean addAll(Collection<? extends E> c)
        /**
         * Appends all of the elements in the specified collection to the end of
         * this list, in the order that they are returned by the
         * specified collection's Iterator.  The behavior of this operation is
         * undefined if the specified collection is modified while the operation
         * is in progress.  (This implies that the behavior of this call is
         * undefined if the specified collection is this list, and this
         * list is nonempty.)
         *
         * @param c collection containing elements to be added to this list
         * @return <tt>true</tt> if this list changed as a result of the call
         * @throws NullPointerException if the specified collection is null
         */
        public boolean addAll(Collection<? extends E> c) {
            // 集合转为Object[] 即使底层数组不是Object也没关系,会将数组元素复制到list的Object[]
            Object[] a = c.toArray();
            int numNew = a.length;
            ensureCapacityInternal(size + numNew);  // Increments modCount
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
            return numNew != 0;
        }
    
    • boolean addAll(int index, Collection<? extends E> c)
        /**
         * Inserts all of the elements in the specified collection into this
         * list, starting at the specified position.  Shifts the element
         * currently at that position (if any) and any subsequent elements to
         * the right (increases their indices).  The new elements will appear
         * in the list in the order that they are returned by the
         * specified collection's iterator.
         *
         * @param index index at which to insert the first element from the
         *              specified collection
         * @param c collection containing elements to be added to this list
         * @return <tt>true</tt> if this list changed as a result of the call
         * @throws IndexOutOfBoundsException {@inheritDoc}
         * @throws NullPointerException if the specified collection is null
         */
        public boolean addAll(int index, Collection<? extends E> c) {
            rangeCheckForAdd(index);
    
            Object[] a = c.toArray();
            int numNew = a.length;
            // 扩大数组
            ensureCapacityInternal(size + numNew);  // Increments modCount
    
            int numMoved = size - index;
            // 如果插入位置index不在末尾
            if (numMoved > 0)
                // 移动index及以后的元素,将index--index+numNew-1的位置空出来
                System.arraycopy(elementData, index, elementData, index + numNew,
                                 numMoved);
            // 将集合数组元素插入到指定位置index
            System.arraycopy(a, 0, elementData, index, numNew);
            size += numNew;
            return numNew != 0;
        }
    

    注意

    • 添加单个元素时,并不会在每次添加时扩容,需要满足size==elementData.length时才会扩容,容量为1.5倍旧容量或更高

    add()源代码中使用ensureCapacityInternal(size + 1),需要满足size+1>elementData.length才会准备扩容。需要注意默认构造的arraylist实例首次扩容为10,之后才是1.5倍或更高

    • 插入指定位置需要额外的移动之后的元素造成很大的开销

    删除

    • void clear()
        /**
         * Removes all of the elements from this list.  The list will
         * be empty after this call returns.
         */
        // 此实现将数组所有数组元素置为null
        public void clear() {
            modCount++;
    
            // clear to let GC do its work
            for (int i = 0; i < size; i++)
                elementData[i] = null;
    
            size = 0;
        }
    
    • E remove(int index)
        /**
         * Removes the element at the specified position in this list.
         * Shifts any subsequent elements to the left (subtracts one from their
         * indices).
         *
         * @param index the index of the element to be removed
         * @return the element that was removed from the list
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public E remove(int index) {
            // 检查index的上界
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
            // 剩余的元素数量即需要被向前移动一位的元素数量
            int numMoved = size - index - 1;
            // 如果指定索引不是最后一个元素(size-1),就左移index后的元素一位到index
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            // 将最后一个元素置为null,删除
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }
    
    • boolean remove(Object o)
        /**
         * Removes the first occurrence of the specified element from this list,
         * if it is present.  If the list does not contain the element, it is
         * unchanged.  More formally, removes the element with the lowest index
         * <tt>i</tt> such that
         * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
         * (if such an element exists).  Returns <tt>true</tt> if this list
         * contained the specified element (or equivalently, if this list
         * changed as a result of the call).
         *
         * @param o element to be removed from this list, if present
         * @return <tt>true</tt> if this list contained the specified element
         */
        public boolean remove(Object o) {
            if (o == null) {
                for (int index = 0; index < size; index++)
                    if (elementData[index] == null) {
                     // 通过左移来删除元素,仅减少了范围检查和返回值获取
                        fastRemove(index);
                        return true;
                    }
            } else {
                for (int index = 0; index < size; index++)
                    if (o.equals(elementData[index])) {
                        // 通过左移来删除元素,仅减少了范围检查和返回值获取
                        fastRemove(index);
                        return true;
                    }
            }
            return false;
        }
    
        /*
         * Private remove method that skips bounds checking and does not
         * return the value removed.
         */
        private void fastRemove(int index) {
            modCount++;
            int numMoved = size - index - 1;
            // 如果index不是最后一个元素索引就将index后的元素向左移动一位
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
        }
    
    • boolean removeAll(Collection<?> c)
        /**
         * Removes from this list all of its elements that are contained in the
         * specified collection.
         */
        public boolean removeAll(Collection<?> c) {
            Objects.requireNonNull(c);
            return batchRemove(c, false);
        }
    
    • boolean retainAll(Collection<?> c)
        /**
         * Retains only the elements in this list that are contained in the
         * specified collection.  In other words, removes from this list all
         * of its elements that are not contained in the specified collection.
         */
        public boolean retainAll(Collection<?> c) {
            Objects.requireNonNull(c);
            return batchRemove(c, true);
        }
    

    私有方法:

        /**
         * 如果complement==false,删除list中包含在collection的元素
         * 如果complement==true,删除list中不包含在指定collection中的元素
         * @param c
         * @param complement
         * @return
         */
        private boolean batchRemove(Collection<?> c, boolean complement) {
            final Object[] elementData = this.elementData;
            // r用于在原数组中遍历,w用于在保留到原数组的下标,从w后的元素将被覆盖为null删除
            int r = 0, w = 0;
            boolean modified = false;
            try {
                // 遍历此list数组元素
                for (; r < size; r++)
                    // 如果指定collection的元素存在于此list中   根据complement策略选择是否保留到数组中
                    if (c.contains(elementData[r]) == complement)
                        // 在原数组中从0开始覆盖为新元素
                        elementData[w++] = elementData[r];
            } finally {
                // Preserve behavioral compatibility with AbstractCollection,
                // even if c.contains() throws.
                // 由于collection可能会跑出异常,try-finally保证在异常抛出后仍然可以正常删除
                // 如果抛出异常,在该元素后的所有元素将会被正常保留,不管是否在存在于collection中
                if (r != size) {
                    System.arraycopy(elementData, r,
                                     elementData, w,
                                     size - r);
                    w += size - r;
                }
                // 将数组保留元素之后的未覆盖的原数组元素置为null
                if (w != size) {
                    // clear to let GC do its work
                    for (int i = w; i < size; i++)
                        elementData[i] = null;
                    // 数组元素改变的次数应该反应到修改计数器
                    modCount += size - w;
                    size = w;
                    modified = true;
                }
            }
            return modified;
        }
    
    • boolean removeIf(Predicate<? super E> filter)
        // 移除在list中所有符合条件的元素
        // 通过BitSet保存符合条件的索引,再元素复制循环中跳过bitSet为true的索引元素
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            Objects.requireNonNull(filter);
            // figure out which elements are to be removed
            // any exception thrown from the filter predicate at this stage
            // will leave the collection unmodified
            int removeCount = 0;
            // 创建一个从0--size-1索引的bitSet,用于表示在原数组中需要删除的索引位置
            final BitSet removeSet = new BitSet(size);
            final int expectedModCount = modCount;
            final int size = this.size;
            // 遍历list的数组找出符合条件要删除元素的索引位置
            for (int i=0; modCount == expectedModCount && i < size; i++) {
                @SuppressWarnings("unchecked")
                final E element = (E) elementData[i];
                // 如果数组中存在元素符合条件filter
                if (filter.test(element)) {
                    // 设置bitSet对应索引位置为true
                    removeSet.set(i);
                    removeCount++;
                }
            }
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
    
            // shift surviving elements left over the spaces left by removed elements
            // 如果存在符合条件的元素
            final boolean anyToRemove = removeCount > 0;
            if (anyToRemove) {
                // 新的元素个数
                final int newSize = size - removeCount;
                // 遍历原数组并将保留的元素移动到数组最左边
                for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                    // 返回从当前索引i开始bit为false的索引  如:{0:false, 1:true, 2:false]
                    // nextClearBit(0)返回0,nextClearBit(1)返回2
                    // 跳过符合条件的即被删除的元素索引
                    i = removeSet.nextClearBit(i);
                    elementData[j] = elementData[i];
                }
                // 将未覆盖的旧元素置为null
                for (int k=newSize; k < size; k++) {
                    elementData[k] = null;  // Let gc do its work
                }
                this.size = newSize;
                if (modCount != expectedModCount) {
                    throw new ConcurrentModificationException();
                }
                modCount++;
            }
    
            return anyToRemove;
        }
    

    注意

    • 所有删除操作最后都是将数组元素左移覆盖原数组元素,然后将未覆盖掉的位置置为null

    • void clear()不是简单的将elementData替换为新数组,而是将所有元素置为null来达到清空效果

    • 在removeAll()和retainAll()允许collection与list元素类型存在异常时仍然能够正确的删除异常前的元素,异常元素即后面的元素会仍然存在于list中

    这两个方法的私有实现batchRemove(Collection<?> c, boolean complement)的collection.contains()在与list的数组比较时,如果contains()抛出异常不会中断该方法,只是异常后的元素默认保留在list中,即使存在与collection中也不会删除list对应的元素

    • Java8新增的方法removeIf()的实现与batchRemove()大同小异,都是在原数组中找到指定的元素下标,然后移动整个数组元素。前者找到所有指定元素后在移动,后者是边找边移动

    removeIf的实现使用BitSet记录满足条件的元素下标,并不会处理异常,如果Predicate.test()抛出异常会停止方法运行。另外在modCount用for验证更加频繁。支持函数式编程:

        static void removeIfTest() {
            List<String> list = new ArrayList<>(5);
            list.add("s");
            list.add("st");
            list.add("str");
            // 不能使用==比较,虽然指向同一个常量池对象
    //        list.removeIf(s-> s == "st");
            list.removeIf(s-> s.equals("st"));
            // 输出:[s, str]
            System.out.println(list);
        }
    

    迭代

    • void forEach(Consumer<? super E> action)
        // 对Iterable的每个元素执行给定的操作。按照迭代器的顺序迭代元素
        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            final int expectedModCount = modCount;
            // 由于类型擦除的机制,实际并没有转型为E,仍然为Object[]
            @SuppressWarnings("unchecked")
            final E[] elementData = (E[]) this.elementData;
            final int size = this.size;
            // 遍历元素数组并检查结构修改的可能
            for (int i=0; modCount == expectedModCount && i < size; i++) {
                action.accept(elementData[i]);
            } 
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
        }
    
    • Iterator<E> iterator()
        public Iterator<E> iterator() {
            return new Itr();
        }
    
    • ListIterator<E> listIterator()
        public ListIterator<E> listIterator() {
            return new ListItr(0);
        }
    
        public ListIterator<E> listIterator(int index) {
            if (index < 0 || index > size)
                throw new IndexOutOfBoundsException("Index: "+index);
            return new ListItr(index);
        }
    

    迭代器实现

        /**
         * An optimized version of AbstractList.Itr
         */
        private class Itr implements Iterator<E> {
            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;
                    /**
                     *  next调用后cursor+1=lastRet,previous调用后cursor=lastRet
                     *  next后删除需要光标移后一位即cursor=lastRet,previous后删除不需要移动光标
                     *  但是,cursor=lastRet也没有错误,同时减少的每次的比较操作,算是优化
                     */
    //                if (lastRet < cursor)
    //                    cursor--;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
            // 用于在当前迭代器位置下对剩下的所有元素执行指定的动作
            @Override
            @SuppressWarnings("unchecked")
            public void forEachRemaining(Consumer<? super E> consumer) {
                Objects.requireNonNull(consumer);
                final int size = ArrayList.this.size;
                int i = cursor;
                if (i >= size) {
                    return;
                }
                final Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                // 循环遍历和检查线程修改
                while (i != size && modCount == expectedModCount) {
                    consumer.accept((E) elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
        /**
         * An optimized version of AbstractList.ListItr
         */
        private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                super();
                cursor = index;
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor - 1;
            }
    
            @SuppressWarnings("unchecked")
            public E previous() {
                checkForComodification();
                int i = cursor - 1;
                if (i < 0)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i;
                return (E) elementData[lastRet = i];
            }
    
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.set(lastRet, e);
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;
                    ArrayList.this.add(i, e);
                    cursor = i + 1;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    

    注意

    • ArrayList的迭代器与AbstractList的实现并没有过大的区别

    相比之下,前者的next()和previous()增加了两层检查:对下标i过界抛出nosuchelement和concurrentmodifi异常,而对remove()减少了一次比较

    • ArrayList迭代器与使用for相比主要性能差别在与迭代器需要多重检查和方法调用, 实际都是通过下标访数组元素
    • 与直接的for相比foreach()更加方便和安全,后者提供快速失败的功能
    • 对于在foreach()中的数组转型的疑问,实际上Object[]底层数组是不能被转型的,但是无边界范型仍然为object,因此允许转型
        //  协变类型不能转型Object
        @SuppressWarnings("unchecked")
        static <T extends Number> void arrayConvertTest(T t) {
    //        Object[] objs = {"t", "tt"};
            Object[] objs = {1, 2};
            System.out.println(objs.getClass().getName());
            // 异常:java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Number;[Ljava.lang.Object;
            T[] ts = (T[]) objs;
            System.out.println(ts.getClass().getName());
        }
    

    替换

    • E set(int index, E element)
        public E set(int index, E element) {
            rangeCheck(index);
    
            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
        }
    
    • void replaceAll(UnaryOperator<E> operator)
        // 将此列表中的每个元素替换为指定操作中应用的结果。
        @Override
        @SuppressWarnings("unchecked")
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            final int expectedModCount = modCount;
            final int size = this.size;
            // 遍历所有元素并检查线程修改
            for (int i=0; modCount == expectedModCount && i < size; i++) {
                elementData[i] = operator.apply((E) elementData[i]);
            }
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }
    

    注意

    • 对于线程修改modCount的检查replaceAll的实现与removeIf均一致,都是在for中每次循环检查一次
    • 关于replaceAll(UnaryOperator<E> operator)的使用
        static void replaceTest() {
            List<String> list = new ArrayList<>();
            list.addAll(Arrays.asList("a", "ab", "abc"));
            /**
             * UnaryOperator用于指定参数和返回同类型的结果
             * UnaryOperator<T> extends Function<T, T>:T apply(T t)
             */
            list.replaceAll(s -> {
                // 输出list每一个元素
                System.out.println(s);
                return s.toUpperCase();
            });
            System.out.println(list);
        }
    

    UnaryOperator<E>表示单操作数返回同类型的结果的函数操作,List包含的类型就只允许replaceAll返回同样的类型

    SubList

    • List<E> subList(int fromIndex, int toIndex)
        public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);
            return new SubList(this, 0, fromIndex, toIndex);
        }
    

    私有实现:

        static void subListRangeCheck(int fromIndex, int toIndex, int size) {
            if (fromIndex < 0)
                throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
            if (toIndex > size)
                throw new IndexOutOfBoundsException("toIndex = " + toIndex);
            if (fromIndex > toIndex)
                throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                                   ") > toIndex(" + toIndex + ")");
        }
        /**
            与AbstractList的实现类似,均为在偏移量offset上调用原list对应方法,对于
            subList.listIterator()的实现也只是在AbstratList的基础上将原ArrayList的
            listIterator对象的调用转而自己实现,但是该实现绝大部分ArrayList.ListIterator相同
        */
        private class SubList extends AbstractList<E> implements RandomAccess {
    

    注意

    • SubList extends AbstractList<E> implements RandomAccess不是ArrayList类型,不能把SubList转化为ArrayList,会报异常:java.lang.ClassCastException: src.java.util.ArrayList$SubList cannot be cast to src.java.util.ArrayList
        static void subListTest() {
            List<String> list = new ArrayList<>(3);
            list.addAll(Arrays.asList("a", "ab", "abc"));
            List<String> subList = list.subList(0, 2);
            // class java.util.AbstractList
            System.out.println(subList.getClass().getSuperclass());
            // java.util.ArrayList$SubList
            System.out.println(subList.getClass().getName());
            subList.forEach(s -> System.out.println(s));
            // 异常
    //        ArrayList<String> aList = (ArrayList<String>)subList;
        }
    
    • 具体实现请参考AbstractList和源码,两者大同小异

    其他

    关于构造器ArrayList(Collection<? extends E> c)源代码中"c.toArray might (incorrectly) not return Object[] (see 6260652)"的看法

        static void toArrayTest() {
            String[] strs = {"s", "t", "r"};
            List<String> list = Arrays.asList(strs);
            System.out.println(list.toArray());
            System.out.println(list.toArray(new Object[strs.length]));
          /*输出:
            [Ljava.lang.String;@15db9742
            [Ljava.lang.Object;@6d06d69c
          */
        }
    

    当使用Arrays.asList()将数组转化为List接口时,list.toArray()的行为不一致,导致直接将数组类型仍然为原数组类型String,下面是Arrays.asList()内部实现ArrayList.toArray()方法源码,;直接clone(),所以才保持原数组类型。

            @Override
            public Object[] toArray() {
                return a.clone();
            }
    

    这个bug应该在Java 9中已修复

    参考:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652

    相关文章

      网友评论

          本文标题:ArrayList

          本文链接:https://www.haomeiwen.com/subject/ynabsqtx.html