文章中用到的代码是基于JDK1.7.0_79
ArrayList是有序、可以重复、可以为null的数据集合,底层是使用数组进行实现的,数组容量可以自动增长,除了实现List接口外,还提供了其他的一系列操作数组的方法。
ArrayList实现了Serializable接口,因此它可以序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。
ArrayList本质是数组
transientObject[] elementData;
ArrayList有提供三个构造方法
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
public ArrayList(Collection c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
第一个构造方法,设置初始化大小,第二个构造方法,指定了数据为空数组,默认容量大小是10,第三个是构造一个包含指定collection的元素的列表。
添加数据有以下几个方法
//该方法是用指定的对象替换数组中指定位置的对象,并且返回原来的对象
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
//该方法是在数组末尾添加一个对象
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!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//该方法是将指定的Collection对象插入到数组的末尾
public boolean addAll(Collection c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
//该方法是将指定的Collection对象插入到指定位置,如果指定位置后面还有对象,后面的对象依次后移
public boolean addAll(int index, Collection c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
读取数据有以下一个方法,返回指定位置的一个元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
删除一个对象有两个方法,分别可以根据数组下标和对象进行删除,如果是根据数组下标进行删除的话,会返回被删除的对象,如果是根据对象进行删除,返回的是boolean,因为ArrayList允许重复,一个实例中可能有多个一样的对象,如果是根据对象进行删除,只会删除数组中第一次出现的对象,删除一个对象之后,该对象所在位置后面如果还有对象,后面的对象都会向左移动一个位置。
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;
}
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;
}
清空数据有2个方法,分别是clear()和removeAll(Collection c);
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
clear()在循环遍历ArrayList,并且将每一个元素都置为null,使它们在没有被外部引用的情况下可以被垃圾回收。
public boolean removeAll(Collection c) {
return batchRemove(c, false);
}
private boolean batchRemove(Collection c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
//删除的话,complement=false,如果该对象不在指定的集合中,则将该对象放在数组前面,遍历完成之后,elementData数组中w后面的对象是跟前面重复了,可以直接删除
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
//如果在上面遍历过程中出现异常,把未被遍历的数据接到后面
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//如果w!=size,说明有对象被删除了,则从w开始到后面的元素都直接设置为null,等待gc回收
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
removeAll可以只删除部分数组,这个方法会检查数组中每个对象是否包含在特定的集合中。如果存在,则去掉。因为会用到contains方法,removeAll的复杂度是O(n^2)。所以在想要重置一个大的ArrayList时,这种方法是绝对不可取的。
根据以上代码分析,如果是删除所有数据,使用clear()方法效率会更高。
从上面的代码可以看出,每次在调用add()方法添加对象的时候,都会调用方法ensureCapacityInternal(size + 1)
private void ensureCapacityInternal(int minCapacity) {
//如果当期数组是空的,则设置容量长度取默认长度与传进来长度的最大值,默认长度是10
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//如果minCapacity的值大于add数据之前的大小,就调用grow方法,进行扩容,否则什么也不做。
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//设置新的容量长度为原来长度的1.5倍(JDK1.6是1.5倍+1)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//检查新容量是否超出了ArrayList所定义的最大容量,若超出了,则调用hugeCapacity()来比较minCapacity
//和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为ArrayList定义的最大容量,
//否则,新容量大小则为 minCapacity。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//数组拷贝,每次调用add(),都需要拷贝一次数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
每次调用add()方法后,数组容量就会扩大1.5倍,所以如果事先能够确定数组长度的话,就直接设置好长度,避免每次调用add的时候进行数组重新拷贝以及内存开销。
ArrayList的clone()是浅拷贝,返回当前ArrayList实例的一个副本,对副本进行添加或者移除对象,不影响原来的ArrayList对象
public Object clone() {
try {
@SuppressWarnings("unchecked")
ArrayList v = (ArrayList) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
ArrayList a = new ArrayList();
People p1 = new People(20,"aaa");
People p2 = new People(30,"bbb");
a.add(p1);
a.add(p2);
ArrayList b = (ArrayList) a.clone();
b.remove(p2);
System.out.println(a);
System.out.println(b);
[ArrayListTest$People@f0d1e0b, ArrayListTest$People@262f6be5]
[ArrayListTest$People@f0d1e0b]
如果有误,欢迎指正
网友评论