ArrayList
存储结构
elementData
作为 ArrayList 的数据存储结构,用于储存该对象堆中的引用。在 elementData 为空时,该数组将赋值为常量 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
。
transient 修饰的
elementData
不会被defaultWriteObject()
自动序列化,而是通过重写writeObject()
方法,再遍历该数组的有效数据再逐个序列化,这样做的目的是该数组的长度并不是当前 ArrayList 的长度,而是做了适当的冗余(下文有解释),自动序列化难免有效率的损失。
private static final Object[] DEFAULTCAPACITY_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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
add
add()
中提到 elementData
,和 ensureCapacityInternal()
方法,我们逐个了解。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
显然,ArrayList 作为一个可动态修改的对象,修改 其存储结构 elementData
的长度是必须要解决的问题,ensureCapacityInternal()
方法便是实现该功能的具体方法。
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureCapacity()
方法保证 elementData
为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
时,minCapacity 的值大于等于缺省的 DEFAULT_CAPACITY
(值为 10)。否者参数 minCapacity 的值只要的大于 0 都是可行的,由于这个 public 方法,在 ArrayList add()
对象数量多是时候,可调用 ensureCapacity()
提前拓展数组,得到效率的提高。
ensureCapacityInternal()
方法同样对 elementData 是否为缺省值做判断,如果是的话,minCapacity 的值同样要大于等于缺省的 DEFAULT_CAPACITY
(值为 10)。
ensureExplicitCapacity()
是拓展数组前最后一个调用, 递增了计数器 modCount
,并在需要拓展时,调用了 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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
定义 newCapacity 值为旧值的 1.5 倍,取 minCapacity 与 newCapacity 较大的值。 但如果这个值大于 MAX_ARRAY_SIZE
值,则调用 hugeCapacity()
判断是否 overflow。最后调用 Arrays.copyOf()
拷贝一份对应大小的数组赋值给 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;
}
remove
方法比较简单,获取到索引的对象后,将elementData
数组 index + 1 后的数据前移一位,将多出的最后一位对象至 null。
ArrayList 的优缺点
- 优点
- ArrayList底层以数组实现,是一种 随机访问 模式,再加上它实现了RandomAccess 接口,因此查找也就是 get 的时候非常快.
RandomAccess 作为标记接口(Marker interface),表示它能够快速随机访问存储的元素。
在源码的注释中存在以下规则,当get()
方法获取对象的速率高与iterator.next()
的速率,可以标记该接口。
/**
* should generally implement this interface. As a rule of thumb, a
* <tt>List</tt> implementation should implement this interface if,
* for typical instances of the class, this loop:
* <pre>
* for (int i=0, n=list.size(); i < n; i++)
* list.get(i);
* </pre>
* runs faster than this loop:
* <pre>
* for (Iterator i=list.iterator(); i.hasNext(); )
* i.next();
* </pre>
- 缺点
- 在删除元素是,需要执行一次
System.arrayCopy()
- 在添加元素时,如需拓展
elementData
长度时,需要执行一次System.arrayCopy()
- ArrayList 没有为任务方法添加同步,所以肯定不会是线程安全的容器
Collections.synchronizedList(anylist)
可以将你的 List 转化为线程安全的。另外 vector 也是一个线程安全的 list。
ArrayList 与 Vector 的区别
Vector 与 ArrayList 的实现极其相似,通过其 add()
方法可以知道除了在增加了同步以外,其他实现几乎一致。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
- Vector 是线程安全的
- Vector可以指定增长因子,在扩容时会在原容量的增加增长因子的值。