ArrayList集合是基于数组实现的,现在我们一起来看下JDK1.7中的源码:
/**
* 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.
*/
private transient Object[] elementData;
这里,可以清晰的看到arrayList的元素是存储在一个数组缓冲区。第一次插入元素,如果数组没有定义长度,会默认增长到10,也可以通过构造器自定义初始长度。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
由于list本质是数组构成,但可以实现动态扩展,每次扩展是将元素放入一个大小是之前1.5倍的新数组中。这里扩容由ensureCapacity()方法来判断。
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(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;
//这里做了扩容1.5倍的处理
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果扩容1.5倍还是比元素个数少,那么直接容量增加到需求容量大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//数组拷贝,直接将旧数组中所有元素复制到固定长度的新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
下面看下add和remove方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!确定是否需要扩容
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//在原数组中将元素进行移动,移动size-index个元素
System.arraycopy(elementData, index, elementData, index + 1,size - index);
elementData[index] = element;
size++;
}
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;
}
删除第一个出现的o对象:
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
}
这里可以清晰的看到不管是add还是remove是需要对数组进行复制移动,add更是需要扩容判断,如果需要扩容,需要数组拷贝。这些操作都是极其耗时的。
下面查看下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;
}
List是基于数组的,根据索引,可以很方便的索引为index处的元素。
下面查看下contains方法:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
可以清晰的看到,contain方法是对数组中元素进行了遍历操作,如果查找元素位置比较靠后,时间复杂度接近O(n).
对ArrayList的总结:
1.排列有序,可以重复
2.底层实用数组
3.查询速度快,增删慢
4.线程不安全
5.当容量不够,ArrayList扩容到当前的1.5倍