本文主要写了关于ArrayList的添加和删除以及容量扩充的源码解析,更多的方法源码解析将在之后更新
http://blog.youkuaiyun.com/a1n9n7e/article/details/77619471
ArrayList是实现了基于动态数组的数据结构
add( ) 方法的实现
//在数组尾部添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保内部数组有足够空间
elementData[size++] = e; //将元素加入到数组的末尾,完成添加
return true;
}
//在数组指定位置添加元素
public void add(int index, E element) {
rangeCheckForAdd(index); //确认插入的index范围,如果index > size || index < 0,则报异常
// 确保内部数组有足够空间
ensureCapacityInternal(size + 1);
//从原数组的index位置开始复制,
//从目标数组的index+1位置开始将元素放入,复制元素的数量为size - index
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素加入到数组的index指定位置,完成添加
elementData[index] = element;
//数组游标增加
size++;
}
这里我们可以看出,调用add() 方法时,都会进行一次数组的复制,如果仅在数组尾部添加数据是比较方便的,但当向数组中指定位置添加数据,且添加位置越靠前时,数组重组的开销也会越大,对比LinkedList添加元素的性能,ArrayList的添加性能明显偏低。
扩容 ensureCapacityInternal( )方法的实现
private void ensureCapacityInternal(int minCapacity) {
//如果当前内部数组为空,没有存放任何元素时
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//设置初始容量值,取DEFAULT_CAPACITY和 minCapacity的最大值
//默认DEFAULT_CAPACITY = 10,
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//扩展
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; //原有内部数组容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //扩充为原有内部数组容量的1.5倍
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:
//通过copyOf将原数组复制到新的数组中
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;
}
删除指定位置元素
根据传入的是指针index 删除该位置上的数组元素,和add 方法相似
public E remove(int index) {
//确认指针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;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
删除数组中第一个出现的Object对象
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 remove method that skips bounds checking and does not
* return the value removed.
*/
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
}
对比LinkedList
ArrayList和LinkedList的大致区别如下:
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
总结:
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
3.LinkedList不支持高效的随机元素访问。
4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。