概述:
ArrayList是一个重要的Java集合,底层基于数组实现,用以解决数组的一系列操作。
性质:
一、非同步
ArrayList与Vector类似,但与Vector不同的是,ArrayList未实现同步。
说明文档里提到:“This class is roughly equivalent to Vector, except that it is unsynchronized.”。
在Vector中,我们可以发现机会所有的方法都有synchronized修饰,而在ArrayList中你将找不到这种修饰。
对于非同步性质的相关原理及测试,可以查看我的上一篇文章
二、顺序容器,可以放入null元素
顺序容器自然不必说:元素存放的数据与放进去的顺序相同。
有趣的是,它可以放入null元素。而它对null元素的查找和删除等,是通过特处理来实现的,我们观察下面这个remove()方法的代码,可以很容易解释。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);//fastRemove在后面介绍
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
三、capacity容量 与 grow()
capacity代表实例的实际大小,容器内存储元素的个数不能多于当前容量。若容量不足,会通过grow()方法通过修改capacity来进行扩容。
grow()方法:
private void grow(int minCapacity){…},
Parameters:
minCapacity the desired minimum capacity//所需的最小容量
这个grow方法,首先进行(1.5*oldCapacity)与(minCapacity)的比较,如果1.5倍的原容量已经足够,那会使用这个容量扩容,否则采用minCapacity进行扩容。
至于扩容的过程,其实是通过Array.copyOf()新建的对象,不是在原对象的废墟上加盖。
表现在程序上是这样:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
/**(1.5*oldCapacity)与(minCapacity)的比较 */
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);
}
主要方法分析:
size(),isEmpty()
常数,直接与参数size进行操作比较
get(int Index),set(int Index,Object o)
常数,对index检查越界后,直接对对应坐标元素进行操作
get中值得注意的一点是:因为底层是以Object实现的,得到的元素进行了转换,才得到我们需要的类型:
return (E) elementData[index];//注意类型转换
IndexOf(Object o),lastIndexOf(Object o)
复杂度与目标位置有关
查找第一个值相等的对象( .equal() ),查找最后一个值相等的对象,返回坐标
区别在于遍历顺序
满足null元素查找。
add()
add方法有很多,不过原理都差不多,复杂度方面也与插入数目及位置有很大关系,但总归是线性的。
判断容量是否充足,试着通过前面介绍的grow()进行扩容。
通过Index进行计算,朝后复制,复制调用的是原生类System.arraycopy()。(有趣的是这个原生类可以自我复制)
进行覆盖填充
通过源码看一看:
public void add(int index, E element) {
/** 检查坐标 */
rangeCheckForAdd(index);
/** 查看容量,并尝试扩容 */
ensureCapacityInternal(size + 1); // Increments modCount!!(这句是关于非同步的注释)
/** 向后复制 */
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
/** 覆盖赋值 */
elementData[index] = element;
size++;
}
具体来看:
add(Object o)直接加到最后
add(int Index,Object o)加到最后指定位置
add(Collection c) 直接对容器c进行了System.arraycopy(),将容器里所有元素,直接加到原elementData末尾。
add(int Index,Collection c)与上一个类似,不过指定了插入位置。
remove()
作为add()的逆操作,删除操作其实就是“向前复制”,但要想让GC起作用,还得手动将无效值,也就是最后一个位置赋null值。
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; //清除该位置的引用,让GC起作用
return oldValue;
}
remove()方法有两个版本,
一个是remove(int index)删除指定位置的元素,返回删除的元素
另一个是remove(Object o)删除第一个满足o.equals(elementData[index])的元素。