jdk8中ArrayList源码分析

1.构造方法分析

ArrayList初始化时,可以设定数组初始容量,不设定则数组默认初始容量为0,即一个空的对象数组,或者初始化时传入一个集合,集合中的元素会被复制到ArrayList中的对象数组elementData。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//默认等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA
transient Object[] elementData; // non-private to simplify nested class access

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
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);
    }
}

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 *
 * @param c the collection whose elements are to be placed into this list
 * @throws NullPointerException if the specified collection is null
 */
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;
    }
}

2.Add方法分析

size:	ArrayList中已有元素个数
elementData:ArrayList中存放元素的对象数组

简易原理分析:添加元素时先计算当前需要的容量,
如果ArryList没有设置初始容量且是第一次添加元素,则初始化elementData为长度10的数组;

否则比较size+1和当前elementData的长度,若小于等于则无需扩容直接添加,若大于则进行1.5倍扩容;

若1.5倍扩容仍小于size+1,则直接扩容至size+1大小,这种情况仅有一种,那就是设置ArrayList初始容量为1,当第二次添加元素时,size=1时,此时1.5size仍然为1,由于使用Arrays.copyOf(elementData, 1.5倍size),无法直接扩容成功。

方法调用过程:ArrayList添加元素时,先调用方法ensureCapacityInternal,ensureCapacityInternal中先调用方法calculateCapacity计算当前需要的容量,在将结果传入ensureExplicitCapacity方法,然后ensureExplicitCapacity方法,如果elementData容量足够则无需扩容,若不足则调用grow方法进行扩容。
最后,add方法将元素放进数组末尾。

calculateCapacity方法处理是:
如果elementData还是一个空数组,那么就返回DEFAULT_CAPACITY(值为10)和传入的数minCapacity中大的值;
如果elementData不为空数组,那么就返回传入的数。

ensureExplicitCapacity方法处理是:
如果传入的参数比elementData这个对象数组的长度大,则进行调用grow方法进行扩容;
否则不做处理。

grow方法处理是:
newCapacity 等于elementData数组长度的1.5倍;
如果newCapacity 仍小于传入的参数,那么newCapacity 等于传入的参数,这种情况仅适用于一种,那就是size=1时,此时1.5size仍然为1,即newCapacity =1,此时直接使用Arrays.copyOf达不到扩容效果;
如果newCapacity >MAX_ARRAY_SIZE (最大数组容量),那么调用
hugeCapacity方法;
最后使用Arrays.copyOf方法,将elementData复制到一个newCapacity 长度的新数组并赋值给elementData。

hugeCapacity方法处理是:
如果minCapacity<0,就抛OutOfMemoryError异常,即内存溢出;由于minCapacity是int型,那么会导致minCapacity<0的,只有计算minCapacity时,size+1<0或者size+1>2^31-1这两种情况。
如果minCapacity>MAX_ARRAY_SIZE,则返回Integer最大值(2^31-1);
否则返回MAX_ARRAY_SIZE(2^31-1-8)

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 static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//结构修改次数

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

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 final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

下面这种带索引的add方法原理基本一致,代码比较容易懂,就不说了。

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

3.Remove方法分析

按index移除元素:
首先检查index范围,然后计算需要移动的元素个数,使用System.arraycopy方法从下标为index+1的的元素以及后面的元素都向前移动1位。
接着–size,接着把elementData的size位置为null,正好是未修改之前数组的第size个元素置为null。
elementData数组这里并没有缩容。

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; // clear to let GC do its work

    return oldValue;
}

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

按object移除元素
1.如果object为空,遍历elementData,找到第一个为null的object并移除,elementData数组这里并没有缩容。
2.如果object不为空,遍历elementData,找到第一个object.equals==true的并移除该元素,同样的elementData数组这里并没有缩容。

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

4.get/set方法
代码比较容易懂。

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

总结:

1.ArrayList可以通过构造方法设置初始化容量,也可以直接传入集合,此时初始化容量为集合大小。

2.ArrayList默认初始化容量为0,第一次使用add添加元素时如果未设置初始化容量,则默认会扩容至10,当容量不足时会自动扩容,每次扩容为当前容量的1.5倍,仅当容量为1时,会扩容当前容量为2。

3.ArrayList的add方法在超出当前容量时会扩容,remove方法会通过equals方法比较后移除元素,但不会对数组进行缩容。

4.从源码可以看出ArrayList可以快速的get和set元素,但不适合频繁的add和remove元素,LinkedList则相反。

5.ArrayList不是线程安全的,add和remove、set等修改元素操作时,elementData并未上锁。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值