ArrayList源码

刚开始读jdk源码决定从集合开始,只读一些较为常用重要的部分。

首先是属性:

 

private static final int DEFAULT_CAPACITY = 10;

字面上可以看出其实就是默认的一个容量大小。

 

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

这两个是用于共享的空数组实例。

 

transient Object[] elementData;

是用来存放元素的,所以arrayList底层其实就是数组,transient的作用是这个属性不会被序列化。

 

private int size;

数组列表的大小。

下面是构造函数:

 

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

这个构造函数需要传一个int型参数,通过这个参数来设置arrayList容器的初始化容量大小。

 

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

不传任何参数就会赋值一个空的数组。

 

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

它也可以接受一个Collection或者它的子类,首选会把集合参数转为数组存到elementData里,接着判断elementData数组的元素个数是否等于0,等于0就直接赋值一个空的数组,不等于0判断elementData是否是Object[],最后就是复制了。

arrayList.add():

 

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

首先我们要弄清楚集合的长度和容量的区别,这指的是两个概念。

打个比方,一个电梯可以乘坐10个人,现在里面坐了3个人,那么这个10就指的是容量,而这个3指的是长度。

arrayList添加元素的时候首先会调用ensureCapacityInternal(size+1)方法

 

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

从字面可以看出这个参数就是最小容量的意思,elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA指的应该就是无参构造函数了,而下面的赋值就可以很明确的知道了为什么会初始化容量为10了。

 

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

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

modCount是用来记录list结构修改的次数。

minCapacity-elementData.length>0,这个地方我想了很久,查阅资料最后才明白是什么意思,minCapacity指的是数组的元素数量加1,而elementData.length指的是数组的容量。所以如果大于0返回true,说明数组容量已达最大了。接着就需要扩容了。下面是grow方法

 

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

这里的代码就比较好理解了,首先是获取当前数组长度赋值为一个老的容量,然后定义一个新的容量是老容量的1.5倍,最后判断新容量是否小于最小容量,小于的话就将最小容量赋值给新的容量,如果新容量大于数组最大容量,也就是

 

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

那么执行hugeCapacity方法,最后就是将数组和新容量复制到elementData。

 

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

hugeCapacity中就是对最小容量进行了判断,大于MAX_ARRAY_SIZE就返回int的最大值,否则返回MAX_ARRAY_SIZE,这个一般不会有这么大,就不必深究了。

 

add(int index, E element)
addAll(Collection<? extends E> c)
addAll(int index, Collection<? extends E> c)

都差不多,第一个add理解了,这三个看一下就明白了。

 

remove(int index)
remove(Object o)
fastRemove(int index)

这三个方法看完之后发现一个问题,arrayList的源码对数组进行操作基本都是

 

System.arraycopy(elementData, index+1, elementData, index,
                 numMoved);

最后看看arraylist的清空集合方法

 

public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

其实就是将数组的每个元素设置为空,长度设置为0.

第一次看源码发现没有想象中的那么难,但是我也只是看懂了源码的实现,可能还有很多设计思想需要以后慢慢学习,还需要看更多的源码,继续努力。

 

补充

1、由于ArrayList是基于数组,且每次删除数据,只是把需要删除的数据之后的数据的下标向前移了一位,这样大量删除数据会导致数组中有很多的空,浪费了空间,所以ArrayList提供了一个方法trimToSize() ,这个方法会把ArrayList 实例的容量调整为列表的当前大小。

2、ArrayList的遍历,建议不要在foreach中对集合进行增删,remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值