JAVA ArrayList原理分析

文章中用到的代码是基于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]

如果有误,欢迎指正

你可能感兴趣的:(JAVA ArrayList原理分析)