数据结构之ArrayList

一、先说几句废话

在java中,线性结构的代表就是List,List下分为ArrayList和LinkedList两种不同的存储结构。本偏说ArrayList,其是用数组实现的,所以具有数组的随机访问功能,所以查询或更新的速度很快。而对于插入和删除操作可能都需要移动大量的元素,所以ArrayList的插入删除操作的效率不高。

二、源码(JDK10)

1、ArrayList集合的初始化。

ArrayList底层是以数组作为载体的,所以首先得有个数组

transient Object[] elementData;

这就是ArrayList保存元素的载体,数组的容量capacity就是这个数组的长度

private int size;

此size代表此ArrayList中已有的元素的个数,要和capacity做一个区分,size <= capacity

接下来说初始化

//当无参构造时的默认容量
private static final int DEFAULT_CAPACITY = 10;

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//无参初始化
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 {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

可见当无参初始化的时候,直接把elementData置空。当指定初始容量初始化时候,如果其大于零,那么会直接创建一个指定容量的数组,一步到位。

到这一步可能发现不了两者方式有何不同,但是当增加元素后就恍然大悟了,且看如下。

 
 
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
//当增加一个元素后,判断当前元素是否达到容量,如果没达到,则直接添加,否则要进行扩容操作grow()
private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

//每次扩容后的容量至少当前元素个数+1
private Object[] grow() { return grow(size + 1); } private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); }
private int newCapacity(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);  //默认扩容1.5倍 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); }

(1)当无参数初始化时,增加一个元素。显然肯定满足扩容条件,进行扩容,由代码可见无参数构造的ArrayList当增加第一个元素的时候,容量先会被扩大到默认的10(DEFAULT_CAPACITY),随后添加当元素当个数达到9时,此时再添加那么容量就会再次扩容到15。如此往复

(2) 当指定参数初始化时,那么当元素个数快达到容量时,就会进行扩容。

说明:1、每次扩容的过程伴随着数组的复制,这会产生一定的时间。 2、每次扩容是根据老的容量用右移计算新的容量,因为右移运算符速度快。

对比一下:现在预计要添加1000个元素,如果通过无参构造生成ArrayList的话,那么在后续过程中会扩容13此才能完成存储。如果是有参构造函数并且一开始默认赋值1001的话,此时就会在初始化时候直接分配1001个空间,从而避免了扩容时产生的巨大开销。所以当集合初始化时,最好指定一下容量。

今天先写这么多,后续完了再编辑。

猜你喜欢

转载自www.cnblogs.com/Pridelory/p/10424086.html