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

Java Collections - ArrayList

2016-01-24 17:26 489 查看

ArrayList

ArrayList内部使用动态数组的形式来保存数据,每次添加元素的时候,会插入到数组尾部。数组大小不够时,会扩容。

与ArrayList相关的还有一个LinkedList,内部用链表来存储数据。

初始化

初始化一个ArrayList的方法如下:

ArrayList<Integer> numbers = new ArrayList<>();


如果不指定大小,默认的数组大小是10。

这个类里面定义了几个成员变量

/**
* Default initial capacity.
* 数组的默认大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
* 数组里没有元素时的空数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
* 存储数据的地方,如果调用无参构造函数,开始时指向EMPTY_ELEMENTDATA,添加元素,大小就会扩展到10;如果调用有参构造参数,就会指向一个初始化大小的数据。
*/
private transient Object[] elementData;

/**
* The size of the ArrayList (the number of elements it contains).
* 数组里真实存储的元素个数
* @serial
*/
private int size;

//记录了ArrayList被修改的次数,在高并发的时候,子类可以通过这个参数来判断普通数组时,是不是被其他线程修改了,抛出ConcurrentModificationException。没有深入研究
protected transient int modCount = 0;


添加元素

用add方法往里面添加元素,会加在数据的最尾部。

numbers.add(10);
numbers.add(100);
numbers.add(150);


在添加元素之前,会先判断数据的空间是否充足。

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


ensureCapacityInternal方法会保证数据的大小。

private void ensureCapacityInternal(int minCapacity) {
//EMPTY_ELEMENTDATA是一个空数组,当ArrayList里没有元素时,elementData指向空数组,这个可以不占用空间。
//取默认大小和minCapacity中较大的那个,默认大小是10
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//保证数组的大小
ensureExplicitCapacity(minCapacity);
}

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

// overflow-conscious code
//如果当前elementData的大小比需要的最小容量还小,就要调用grow方法来扩充容量。
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}


grow方法用来扩容,扩大到原始大小的2.5倍。使用了位移的方法来加快运算速度。

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//原始容量的2.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;   //取最小容量
//如果超过了最大容量(Integer.MAX_VALUE - 8),就使用Integer.MAX_VALUE
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);
}


在使用ArrayList的时候,最好估算好大小,分配合适的空间。否则在扩容的时候会频繁的拷贝数据,影响性能。

遍历元素

遍历的方法有两种。使用index遍历和for each遍历。

System.out.println("#1: indexed loop for iteration");
for (int i = 0; i < numbers.size(); i++) {
System.out.println(numbers.get(i));
}

System.out.println("2#: iteration");
for (Integer value : numbers) {
System.out.println(value);
}


它们的性能如何呢?对于ArrayList,index遍历的性能要更优,但是也优不了多少。

在Java里,foreach语法是iterator的变形

for(int i : weights) {
sum += i;
}


等同与:

for(Iterator<Integer> i = weights.iterator(); i.hasNext()) {
sum += i.next();
}


删除元素

在ArrayList删除元素的耗时与位置有关。

删除最后一个元素:直接把数组的大小减1就行。

删除中间某个元素:需要将它后面的部分都拷贝一次,开销比较大。

因此尽量不要在ArrayList上频繁删除元素。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: