arrayList原理分析从如下几个方面进行
1、继承关系
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
从上面的继承关系可以看出,ArrayList实现了List接口,继承AbstractList,而List又实现了Collection接口,另外它还实现了Cloneable和Serializable两个接口,支持克隆和序列化。
2、关键属性
private transient Object[] elementData;
private int size;
ArrayList很简单,它的存储结构就是一个数组结构,只是这个数组是动态数组,在空间不足的情况下可以自动扩容;另外虽然ArrayLIst是支持泛型的,但是底层存储的时候都会存储为Object类型,这个需要注意。
3、关键方法
1)添加元素时调用的grow方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}
在数组添加元素时,需要进行容量检测,若当前数组长度不足以添加新的元素,则调用该方法对数组进行扩容,里面的关键方法是Arrays.copyOf方法,Arrays是一个方法类,提供了很多静态方法供数组使用,下面是它的实现
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
上面的代码没有什么特别的,最终是调用了System.arraycopy方法复制得到一个扩容后的新数组,System.arraycopy是一个native方法,native是java程序和C/C++程序的接口,它直接调用操作系统底层的方法,无需经过jvm转换,效率比java方法高;另外newType.getComponentType()方法需要注意,该方法是获取数组的元素的class对象的方法,利用反射Array.newInstance生成一个具体的对象数组。
2)转换为数组方法
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
toArray有两个方法,一个是带参数的,一个是不带参数的。从上面的分析知道,不带参数的toArray方法,最后返回的是Object对象的数组;带参数的toArray方法,最后返回的是具体参数类型的数组,这个在使用的时候我们就已经注意到了,不带参数的toArray方法转换为具体类型的数组会报错,原因就在toArray方法的实现里面。
3)add方法
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++;
}
在某个位置添加元素,主要涉及到数组的移动,现在我们知道了为什么说数组的添加删除效率没有链表的高吧,因为内部涉及到数组的移位,数组移位的过程即数组拷贝的过程,只是源和目的都是同一个数组。
4)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; // Let gc do its work
return oldValue;
}
在某个位置删除元素,主要也是数组元素位置移动,最终是要拷贝的方式实现,另外就是删除的数组元素会设置为null,防止内存泄露。
总结:arrayList实际上就是一个动态数组,内部的数组位置移动主要是利用system.arraycopy方法.