注释翻译

  • ArrayList是实现List接口的可变大小数组实现。它允许存储所有元素,包括null。与实现了Vector类似,但不提供同步功能。

  • ArrayList支持所有可选的列表操作,并提供了一些方法来操作内部用于存储列表的数组大小。其中,size、isEmpty、get、set、iterator和listIterator等操作都在常数时间内运行。而add操作在摊销的常数时间内运行,即添加n个元素的时间复杂度为O(n)。其他操作的时间复杂度为线性时间,具体而言是相对于列表大小的线性时间。ArrayList的常数因子相对于LinkedList实现较低。

  • 每个ArrayList实例都有一个容量。容量是用于存储列表元素的数组大小,它始终至少与列表大小一样大。当向ArrayList中添加元素时,其容量会自动增长。容量增长策略的细节没有明确定义,只知道添加一个元素的摊销时间代价是常数的。

  • 应用程序可以在添加大量元素之前,通过使用ensureCapacity方法增加ArrayList实例的容量,这样可以减少增量式的重新分配操作。

  • 需要注意的是,ArrayList的实现不是线程安全的。如果多个线程同时访问同一个ArrayList实例,并且至少有一个线程对列表进行结构上的修改,那么必须对列表进行外部同步。结构上的修改包括添加或删除一个或多个元素,或者显式调整后备数组的大小;而仅仅设置元素的值不属于结构上的修改。为了实现线程安全,通常可以在一个自然封装了列表的对象上进行同步。

  • 如果没有这样的对象可用,可以使用Collections.synchronizedList方法来”包装”列表,使其变为线程安全的。最好在创建列表时进行这样的操作,以防止意外地无序访问列表。

    List list = Collections.synchronizedList(new ArrayList(…));

  • ArrayList的迭代器是快速失败(fail-fast)的。如果在迭代器创建后的任何时候,除了通过迭代器自身的remove或add方法之外,以任何方式对列表进行了结构上的修改,迭代器都会抛出ConcurrentModificationException异常。这样,在面对并发修改时,迭代器能够迅速且干净地失败,而不是在未来的某个不确定的时间里冒着任意、不确定的行为风险。

类签名

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

基本属性

private static final int DEFAULT_CAPACITY = 10; // 默认初始容量10

private static final Object[] EMPTY_ELEMENTDATA = {};   // 空的数组

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData; // non-private to simplify nested class access  真正存放数据的

private int size;

源码

构造函数

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;       // 初始化一个空数组
    }

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {          // 初始化一个传入容量大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {        // 初始化容量小于0 ? 笨蛋,其实这里有时候不是有意传入负数,比如传入Integer.MAX_VALUE + 1 那就是负数了啊!!!
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();      // 从一个现成的集合初始化
        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 {        // 初始化空数组
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

扩容方法grow

扩容是一个私有方法。

// 这里是扩容的入口
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) { // 如果修改后的数组容量大于当前的数组长度那么就需要调用grow进行扩容
        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  minCapacity:需要的最小容量
     */
    private void grow(int minCapacity) {    // 扩容1.5倍
        // overflow-conscious code
        int oldCapacity = elementData.length;   // 旧容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);     // 新容量 = 旧容量+旧容量大小/2
        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);
    }
    private static int hugeCapacity(int minCapacity) {  // 尽力保证容量够用
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?     // 极大可能存储数据,如果需要的最小容量更大,那么就用integer最大值
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

add & addAll

add方法没有什么可讲的,
有一点提一下

i++ & ++i

  • i++ 先赋值在运算,例如 a=i++,先赋值a=i,后运算i=i+1,所以结果是a=1
    • ++i 先运算在赋值,例如 a=++i,先运算i=i+1,后赋值a=i,所以结果是a=2
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
    public boolean addAll(Collection<? extends E> c) {
        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;
    }

addAll

    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;                              // numNew 新添加的
        ensureCapacityInternal(size + numNew);  // Increments modCount  看看是否满足最小容量需求,不满足就扩容

        int numMoved = size - index;                      // 需要复制到长度, [2,3,4,5,6,7,8] 在index为1的3位置插入【9,8,7】,size-index=7-1 = 6
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,   // 上面例子: [2,3,4,5,6,7,8,0,0,0]=>[2,  (3,4,5),  3,4,5,6,7,8]
                             numMoved);     // 相比这块代码你得想一会吧,其实简单,需要复制两次,第一次,先把原土著元素占坑位的移到后面去,再把新进来的移进来

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

remove & removeIf

    public E remove(int index) {
        rangeCheck(index);

        modCount++;                     // 涉及数量变更,就要改了!!
        E oldValue = elementData(index); // 先把要删除的元素捞出来在旁边沥干,晾着

        int numMoved = size - index - 1;    // 要拷贝数组的长度 [2,3,4,5,6,7,8] remove index为2的元素4,size=7,size-index-1=7-2-1=4,
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, // 从index加一位置开始,越过index那个元素进行拷贝,就是玩,气不气人!
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work  把尾元素置为空,因为不是整体前移了吗,看写的多好,一个元素也要gc,!!!

        return oldValue;
    }
    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])) { // 这里为什么分开写,因为:null没有.equals哦, 也不能全用==, 因为Integer有缓存哦
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

fastRemove

    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
    }

removeIf

    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;
        final BitSet removeSet = new BitSet(size);
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            final E element = (E) elementData[i];
            if (filter.test(element)) {
                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,j分别是两个指针遍历原数组和 removeSet
                i = removeSet.nextClearBit(i);  // [0,1,2,3,4,5] 移除【3,4】index=(2,3), removeSet{3,4}, i和j为1,2的时候下面的复制语句都正常,是一样的,当j为3的时候,i就不一样了,变成5了:elementData[3] = elementData[5]
                elementData[j] = elementData[i];        // 使用 removeSet.nextClearBit(i) 更新 i 的值。这个方法从索引 i 开始在 removeSet 中查找下一个为0的位。这个位置表示剩余元素的新位置。
            }
            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;
    }

retain & remove

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false); // 和retain是相反的,retain求交集,remove保留当前不在c中的元素
    }

    public boolean retainAll(Collection<?> c) { // 求两个集合的交集
        Objects.requireNonNull(c);  // 为空检查
        return batchRemove(c, true); // 批量删除,删除不存在包含C的元素, 用了contains,加单层for
    }

get

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    E elementData(int index) {
        return (E) elementData[index];
    }

set

    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

站点统计

  • 文章总数:315 篇
  • 分类总数:20 个
  • 标签总数:193 个
  • 运行天数:1156 天
  • 访问总数:40518 人次

浙公网安备33011302000604

辽ICP备20003309号