您的位置:首页 > 编程语言 > Java开发

Java ArrayList&Vector 源代码分析

2016-03-13 09:53 796 查看
ArrayList 对象继承了AbstractList对象,这就是说,ArrayList可以使用迭代器来操作,但是有一点要注意,上一次我们看AbstractList源代码时,知道这个迭代器是快速失败的,也就是说他记录了修改的次数,因此在实现这个ArrayList的添加操作时,我们也需要随时更新这个操作记录。也就是如下这个变量。

protected transient int modCount = 0;


现在从头开始看ArrayList对象的使用。

第一步是构造函数,我们经常使用如下语句来初始化对象。

List<Object> list = new ArrayList<Object>();


实际上调用的构造函数如下:

public ArrayList() {
this(10);
}


这里使用了另外一个构造函数,

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


这个构造函数就是初始化了一个Object数组。并在初始化之前检查了参数范围。我们平时使用的时候,默认都是10.

现在看一下最常用的添加元素操作,源代码如下:

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


这里添加逻辑很简单,就是在数组末尾添加了这个元素。但是添加之前,有一个ensureCapacityInternal操作,这个操作就很重要了。我们看看做什么的?

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


这里首先给父类的modCount变量加1,就是说这个容器又多了一个操作,这个主要用于检查多线程环境来使用的。

另外,这里JDK代码中注释了一句overflow-conscious code,检查了,如果数组末尾的这个下标已经超出了数组长度的话,那么就需要扩展数组的长度了。这里使用了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);
}


这个方法逻辑就是扩展数组的长度,长度扩展逻辑是旧的容量除2,再加上以前的容量,换句话说就是一半一半增加的。这里又判断,如果新的容量还是不够的话,那么就直接将最新的数组下标值赋值给数组长度,再进一步检查,新的容量是否比Integer.MAX_VALUE-8还要大,这里为什么是这个数值,JDK的源码中注释了如下说明:

/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


是说有一些JVM会保存一些头字段存放在数组中。如果清理比这个更大的数组,就会导致内存溢出。所以这里是JVM的限制了。

所以整个添加操作到这里基本就结束了。

看一下删除操作:

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; // Let gc do its work

return oldValue;
}


这里简单说明下删除的逻辑:

判断数组下标的范围是否超过数组容量。

增加操作的记录。

检查删除的元素是否在数组末尾,如果不是,则将index后的元素迁移。

最后一步很重要,就是将删除元素所留下来的位置,置为null,方便GC工作。

这里简单说一句,将变量置为null,我以前一直以为是纯属装B的工作,变量过了作用域自然会被GC清理,但是这里却也这么做了,究其原因,是因为这是数组,不是一个简单的变量。

说明一下Vector对象的实现,这个对象也是实现了AbstractList接口,实现和ArrayList一样,但是有一点不同,就是Vector对象中所有的操作接口都是用了synchronized关键字,也就是说每个操作都给Vector对象实例加锁了。这个和StringBuilder&StringBuffer的差异是一样的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  源代码 java