ArrayList源码阅读笔记

ArrayList

ArrayList继承自AbstractList抽象类,实现了RandomAccess, Cloneable, java.io.Serializable接口,其中RandomAccess是一个标志接口,代表可以支持快速随机访问,实现该接口的类使用for循环比使用迭代器要快,LinkedList并未实现该接口,它用迭代器进行遍历要比ArrayList用迭代器要快。可以用instanceof确定某obj是否实现RandomAccess。Cloneable:标志接口,表示可被克隆;java.io.Serializable可序列化标志接口。(克隆和序列化还能继续研究 。。。


ArrayList是动态的、长度可变的,但底层由长度不可变的数组实现,默认数组大小为10,可以用ArrayList(int lenth)初始化数组长度,也可以使用ensureCapacity(int minCapacity)来预估数据数量而提前扩容(用此方法扩容时ArrayList不能为空数组)。
在进行扩容时多次使用Arrays.copyOf(obj [],newObj [])方法,扩容大小由内部方法newCapacity(int minCapacity)确定:

/**
    * Returns a capacity at least as large as the given minimum capacity.返回至少与给定最小容量相同的容量
    * Returns the current capacity increased by 50% if that suffices.如果满足,则返回当前容量增加50%
    * Will not return a capacity greater than MAX_ARRAY_SIZE unless
    * the given minimum capacity is greater than MAX_ARRAY_SIZE.
    *除非给定的最小容量大于MAX_ARRAY_SIZE,否则不会返回大于MAX_ARRAY_SIZE的容量。
    * @param minCapacity the desired minimum capacity
    * @throws OutOfMemoryError if minCapacity is less than zero
    */
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//原长度
    int newCapacity = oldCapacity + (oldCapacity >> 1);//原长度1.5倍
    if (newCapacity - minCapacity <= 0) {//如果给定长度大于1.5倍
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            return Math.max(DEFAULT_CAPACITY, minCapacity);//为空则返回给定和默认中大的
        if (minCapacity < 0) // overflow不能给负数
            throw new OutOfMemoryError();
        return minCapacity;//否则就返回给定值
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);//这里,MAX_ARRAY_SIZE为Integer.MAX_VALUE-8,因为部分虚拟机会存储head code(头部信息?),所以直接给Integer.MAX_VALUE会报错:数组大小超出VM限制。但它还是会把这8个位置给你233333
}

说完扩容,说缩容,正常情况下开辟过的容量并不会减少,不使用了会赋null(比如clear()方法,以及内部的protected void removeRange()),但这样会造成浪费,因此可以使用trimToSize()方法把其中的数组大小修改为元素数量(trim:修剪)。
其中add、remove方法使用System.arraycopy(Object src, int srcPos,Object dest, int destPos,int length)来创建新的数组,从而表现出长度可变的特性。//这么做代表数组中间不会有null值,但是数组尾部未使用的会有null值。
removeAll(c),可以删除和c相同的元素,也就是求与c的差集;
retainAll(c),可以保留和c相同的元素,也就是求与c的交集。这两个方法都调用了期内部方法`batchRemove方法:

boolean batchRemove(Collection<?> c, boolean complement,
                        final int from, final int end) {
        Objects.requireNonNull(c);//c中不能有null
        final Object[] es = elementData;//获取原数组
        int r;//声明遍历标志位
        // Optimize for initial run of survivors
        //优化幸存者的初始运行,也就是处理需要保留的元素
        for (r = from;; r++) {
            if (r == end)//removeAll与retainAll方法调用时传参from为0,end为数组大小size,这里判断是否需要执行删除操作
                return false;//false代表没有元素删除
            if (c.contains(es[r]) != complement)
                break;//代表需要删除元素
        }
        int w = r++;//声明保留标志位,初始化为第一个要删除的元素,同时r+1继续遍历
        try {
            for (Object e; r < end; r++)
                if (c.contains(e = es[r]) == complement)
                    es[w++] = e;//这里执行的不是删除,而是将后面需要保留的元素覆盖掉第一个需要删除的元素,相当于把需要保留的元素向前移动了
        } catch (Throwable ex) {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            //保留与AbstractCollection的行为兼容性,即使c.contains()抛出也是如此。
            System.arraycopy(es, r, es, w, end - r);
            w += end - r;
            throw ex;
        } finally {
            modCount += end - w;//修改(删除)的次数
            shiftTailOverGap(es, w, end);//拷贝需要保留的元素,同时将后面移动过后的元素赋null
        }
        return true;//代表修改(删除)了元素
    }

该方法还是比较精妙的。

至于迭代器了,恕笔者学识尚浅,就不发表拙见了//待更新。。。


疑点:其中两个私有的io方法,这俩玩意有什么用?私有不说,光见它声明没见它调用啊//待更新。。。

/**
    * Saves the state of the {@code ArrayList} instance to a stream
    * (that is, serializes it).
    *
    * @param s the stream
    * @throws java.io.IOException if an I/O error occurs
    * @serialData The length of the array backing the {@code ArrayList}
    *             instance is emitted (int), followed by all of its elements
    *             (each an {@code Object}) in the proper order.
    */
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioral compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

/**
    * Reconstitutes the {@code ArrayList} instance from a stream (that is,
    * deserializes it).
    * @param s the stream
    * @throws ClassNotFoundException if the class of a serialized object
    *         could not be found
    * @throws java.io.IOException if an I/O error occurs
    */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // like clone(), allocate array based upon size not capacity
        SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
        Object[] elements = new Object[size];

        // Read in all elements in the proper order.
        for (int i = 0; i < size; i++) {
            elements[i] = s.readObject();
        }

        elementData = elements;
    } else if (size == 0) {
        elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new java.io.InvalidObjectException("Invalid size: " + size);
    }
}
posted @ 2019-05-12 16:55  code-blog  阅读(213)  评论(0编辑  收藏  举报