Java高阶:ArrayList源码分析

只谈情不闲聊 提交于 2020-01-06 22:06:15
* ArrayList源码分析
* jdk7和jdk8版本有一些不同

首先,不管7版本还是8版本,ArrayList底层存储没有改变

private transient Object[] elementData;

在使用的时候,最简单的使用方式为:

ArrayList list = new ArrayList();

1.所以首先看ArrayList源码中的空参构造器

    public ArrayList() {
        this(10);
    }

    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

从源码可以看出,空参的时候,初始化的长度就是为10。

即new ArrayList();会创建一个长度为10的Object类型 数组

2.在ArrayList中的add方法:

第一次添加的时候,size大小为0

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

3.在添加数据时候,先回去list的容量是否够用,如果开辟的空间不够用,会执行grow方法进行扩容

private void ensureCapacityInternal(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

4.数组元素扩容方式

扩容后的长度:

oldCapacity:首先记录底层数组扩容前的长度

newCapacity:新的容量大小就是为旧的容量 +  旧的容量 右移一位 

即 新的容量 = 旧的容量 + 旧的容量 / 2 = 1.5 * 旧的容量 (结果使用向下取整)

如果新的容量比需要扩容的容量还要小,直接使用需要扩容的容量作为长度

如果新的容量大于数组最大容量,直接使用Integer.MAX_VALUE作为最大长度

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

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    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.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

扩容后的长度总结:

扩容时,首先会将扩容容量设置为原容量的1.5倍(向下取整)

如果扩容容量还是小于需要的容量,直接使用新的容量长度作为扩容容量

如果新的容量大于数组容量最大值,直接采用Integer.MAX_VALUE作为扩容容量。

容量确定后会讲数据以新的长度拷贝到新的数组中

 

通过以上

建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity);

public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
}

 

在JDK8中

在实例化的时候,底层会初始化一个 {} 的数组,即创建空的Object

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

在第一调用add方法的时候,底层才会开始创建长度为10的数组,后续的添加和扩容操作与jdk7一致。

 

总结

jdk7中的ArrayList使用时候就会创造出长度为10的数组

jdk8中只有在使用开始放入数据才会开始创建数组,会延迟数组的创建时间,节省内存

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!