ArrayList源码分析

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

实现了三个接口。

  • RandomAccess即随机访问,这个接口没有实际意义,可以理解为实现了这个接口标志着该类具有随机访问的功能。在下面的分析中可以看到ArrayList的底层是数组,确实具有随机访问的功能。
  • Cloneable,实现了这个接口必须实现clone方法,其实Object类也具有clone方法,然鹅是不能执行的,会抛出CloneNotSupportedException(除非实现了Cloneable接口)。同时注意Object中的clone方法是浅拷贝。关于深拷贝和浅拷贝不在这里细说,详情可以移步另一篇博客。
  • 序列化接口
 private static final int DEFAULT_CAPACITY = 10;
 
 // 空数组实例
 private static final Object[] EMPTY_ELEMENTDATA = {};
 
 // 空数组中添加第一个元素后扩容至DEFAULT_CAPACITY
 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
 // 设置为transient是因为避免使用java默认的序列化在数组并不满的情况下把数组全部序列化,
 // 使用write/readObject
 transient Object[] elementData; 
初始化

ArrayList的构造函数初始化主要有根据容量和Collection及其子类来进行初始化的方式以及无参初始化三种方式。

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;
    }
}

首先进行数组转换,然后将数组转为Object数组赋给elementData

扩容
public boolean add(E e) {
        // 检查超容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

上边是一个很简单的添加元素的函数,可以看到首先需要检查加入新元素是否会超容

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

可以看到,添加一个元素所要求的最小容量是size+1,我们姑且称之为目标容量。

如果目标容量比现有的数组容量大,就需要扩容操作。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
        
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

扩容的过程:

新容量是旧容量的1.5倍

如果新容量<目标容量,新容量=目标容量

如果新容量>MAX_ARRAY_SIZE,此时的新容量太大,调用 hugeCapacity进行调整新容量

最后完成数组的复制

 private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}

如果目标容量>MAX_ARRAY_SIZE,新容量= Integer.MAX_VALUE ;否则,新容量= MAX_ARRAY_VALUE

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

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; 

        return oldValue;
}

删除的话,当然不需要管扩容的问题,但是删掉中间某个位置的元素势必会引起后边元素挪动,这也是数组的特点。

同时可以看到这里的删除的实现实际上是将某处的对象置为null,即变为弱引用,让GC来清理。具体的引用问题请移步另一篇博客

同时注意添加删除都会导致有一个modCount变量的变化,该变量在其父类AbstractList

protected transient int modCount = 0;

这个主要是用在迭代遍历的时候,引出了ArrayList的一个内部类

private class Itr implements Iterator<E> {
    
    int expectedModCount = modCount;
    
    public E next() {
            checkForComodification();
            // operate something
    }
    
    final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
    }

可以看到modCount在这里发挥作用,迭代遍历元素时,都会比较当前modCount和expectedModCount是否相等,不相等就会抛出ConcurrentModificationException,显然,我们上便看到了,对list进行更改操作就会modCount++,使得其与expectedModCount不相等,很明显,这是ArrayList为了线程安全而设置的一个小trick,所谓的快速失败机制

总结

因为底层是数组,所以ArrayList的操作都很简单,同样需要注意到数组中元素除了查找很快,别的操作都可能需要大量元素的移动,代价较大。同时通过modCount实现了快速失败机制。

关注公众号:java蹲坑读物

跟我蹲坑学习,不错过每一分钟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值