JDK源码阅读笔记-ArrayList

ArrayList通过Object[] elementData保存数据

初始化

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[]
            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

通过Collection初始化时,内部实际调用Arrays.copyOf

@HotSpotIntrinsicCandidate
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

如果类型时Object,则直接传教Object[],否则通过Array.newInstance创建该类型的数组。最后通过System.arraycopy直接调用Cpu指令复制。
可以看出实际把所有的元素都转换成Object类型。这是因为有一个bug
Arrays.asList(x).toArray().getClass() should be Object[].class
该方法有@HotSpotIntrinsicCandidate注解,说明可能被指令直接替换优化

容量

private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //每次扩容在原来的基础上扩大一半
        //要考虑变成负数的情况
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            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);
    }

这个函数咋一看有问题,因为会存在扩容后为负数,则直接返回minCapacity的情况。实际上调用该方法的地方都已经做了判断并且确实minCapacity比实际值大。这样里面判断minCapacity<0的Overflow判断才确实有用。例如

public void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length
            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                 && minCapacity <= DEFAULT_CAPACITY)) {
            modCount++;
            grow(minCapacity);
        }
    }
 private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

    private Object[] grow() {
        return grow(size + 1);
    }

remove相关方法通过System.arraycopy方法移动删除的元素

//删除所有参数集合中的值
 public boolean removeAll(Collection<?> c) {
        return batchRemove(c, false, 0, size);
    }

//保留所有集合中的元素
public boolean retainAll(Collection<?> c) {
        return batchRemove(c, true, 0, size);
    }

boolean batchRemove(Collection<?> c, boolean complement,
                        final int from, final int end) {
        Objects.requireNonNull(c);
        final Object[] es = elementData;
        int r;
        // Optimize for initial run of survivors
        for (r = from;; r++) {
            if (r == end)
                return false;
            if (c.contains(es[r]) != complement)
                break;
        }
        int w = r++;
        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.
            System.arraycopy(es, r, es, w, end - r);
            w += end - r;
            throw ex;
        } finally {
            modCount += end - w;
            shiftTailOverGap(es, w, end);
        }
        return true;
    }

这个batchRemove笔记有意思。complement判断是删是留。假设删除(false).
1.第一个循环找到第一个需要删除的元素,下表记作r.
2.w=r++,这样w指向原来的元素,r指向下一个元素。
3.下一个循环找到不需要删除的元素,es[w++]=e则将不符合条件的元素复制到w处,w指向下一个元素。这样w不断的替换为不需要删除的元素。
4.最后把剩下的元素删除。
这样比申请一个新的数组,把待保留的元素保留到新数组要节省空间。

剩下的就是公共的比如iterator,spliter等一些公共方法。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容