Java ArrayList,本文将介绍Java中真正的动态数组容器类ArrayList。
基本用法
ArrayList是一个泛型容器,新建ArrayList需要实例化泛型参数,比如:
ArrayList<Integer> intList = new ArrayList<Integer>();
ArrayList<String> strList = new ArrayList<String>();
ArrayList的主要方法有:
public boolean add(E e) //添加元素到末尾
public boolean isEmpty() //判断是否为空
public int size() //获取长度
public E get(int index) //访问指定位置的元素
public int indexOf(Object o) //查找元素, 如果找到,返回索引位置,否则返回-1
public int lastIndexOf(Object o) //从后往前找
public boolean contains(Object o) //是否包含指定元素,依据是equals方法的返回值
public E remove(int index) //删除指定位置的元素, 返回值为被删对象
//删除指定对象,只删除第一个相同的对象,返回值表示是否删除了元素
//如果o为null,则删除值为null的元素
public boolean remove(Object o)
public void clear() //删除所有元素
//在指定位置插入元素,index为0表示插入最前面,index为ArrayList的长度表示插到最后面
public void add(int index, E element)
public E set(int index, E element) //修改指定位置的元素内容
这些方法简单直接,就不多解释了,我们看个简单示例:
ArrayList<String> strList = new ArrayList<String>();
strList.add("酷客");
strList.add("教程");
for(int i=0; i<strList.size(); i++){
System.out.println(strList.get(i));
}
输出结果如下:
基本原理
Array-List的基本原理与DynaArray类似,内部有一个数组elementData,一般会有一些预留的空间,有一个整数size记录实际的元素个数(基于Java 7),如下所示:
private transient Object[] elementData;
private int size;
我们暂时可以忽略transient这个关键字。各种public方法内部操作的基本都是这个数组和这个整数,elementData会随着实际元素个数的增多而重新分配,而size则始终记录实际的元素个数。
下面,我们具体来看下add和remove方法的实现。add方法的主要代码为:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
它首先调用ensureCapacityInternal确保数组容量是够的,ensureCapacityInternal的代码是:
private void ensureCapacityInternal(int minCapacity) {
if(elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
它先判断数组是不是空的,如果是空的,则首次至少要分配的大小为DEFAULT_CAPACITY, DEFAULT_CAPACITY
的值为10,接下来调用ensureExplicitCapacity
,主要代码为:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if(minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount++是什么意思呢?modCount表示内部的修改次数,modCount++当然就是增加修改次数,为什么要记录修改次数呢?我们待会解释。
如果需要的长度大于当前数组的长度,则调用grow方法,其主要代码为:
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//右移一位相当于除2,所以,newCapacity相当于oldCapacity的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果扩展1.5倍还是小于minCapacity,就扩展为minCapacity
if(newCapacity - minCapacity < 0)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
代码中已有注释说明,不再赘述。我们再来看remove方法的代码:
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; //将size减1,同时释放引用以便原对象被垃圾回收
return oldValue;
}
它也增加了modCount,然后计算要移动的元素个数,从index往后的元素都往前移动一位,实际调用System.arraycopy方法移动元素。elementData[--size] = null
;这行代码将size减1,同时将最后一个位置设为null,设为null后不再引用原来对象,如果原来对象也不再被其他对象引用,就可以被垃圾回收。
上面的代码中,为便于理解,我们删减了一些边界情况处理的代码,完整代码要晦涩复杂一些,但接口一般都是简单直接的,这就是使用容器类的好处,这也是计算机程序中的基本思维方式,封装复杂操作,提供简化接口。
ArrayList特点
对于ArrayList,它的特点是内部采用动态数组实现,这决定了以下几点。
- 可以随机访问,按照索引位置进行访问效率很高,用算法描述中的术语,效率是
O(1)
,简单说就是可以一步到位。 - 除非数组已排序,否则按照内容查找元素效率比较低,具体是
O(N)
, N为数组内容长度,也就是说,性能与数组长度成正比。 - 添加元素的效率还可以,重新分配和复制数组的开销被平摊了,具体来说,添加N个元素的效率为
O(N)
。 - 插入和删除元素的效率比较低,因为需要移动元素,具体为
O(N)
。
酷客教程相关文章:
评论前必须登录!
注册