ArrayList原理学习(一)
ArrayList集合内置属性
// 默认的数组大小
private static final int DEFAULT_CAPACITY = 10;
// 用于空实例的共享空数组实例。
private static final Object[] EMPTY_ELEMENTDATA = {};
// 共享的空数组实例用于默认大小的空实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 声明一个数组
transient Object[] elementData;
// 用于记录数组里面的元素个数
private int size;
// 数组里面扩容内存的阈值,为Int类型最大范围减去8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 用于记录集合修改的次数 为父类属性
protected transient int modCount = 0;
Serializable接口
Serializable接口为标记接口,只有实现该接口的类才能进行序列化,否则无法序列化,若在不实现该接口进行序列化会报NotActiveException异常。
ArrayList内的writeObject()方法
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// 记录当前序列化开始时的数组修改次数
int expectedModCount = modCount;
// 将ArrayList中除了transient的其他数据序列化
s.defaultWriteObject();
// 将当前数组的元素长度序列化
s.writeInt(size);
// 将数组中元素逐一序列化
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
// 对比记录的修改次数与当前数组修改次数是否相等,如果相等则表明序列化过程中数组未修改
// 不相等着表明序列化过程中数组发生了改变,即抛出异常,停止序列化
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
ArrayList内的readObject()方法
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 声明一个空的数组
elementData = EMPTY_ELEMENTDATA;
// 将序列化的ArrayList读取出来并反序列化
s.defaultReadObject();
// 获取数据长度
s.readInt(); // ignored
if (size > 0) {
// 校验长度 并对数组进行扩容
ensureCapacityInternal(size);
// 声明一个方法内元素
Object[] a = elementData;
// 通过循环将数据逐一反序列化读取出来
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
Cloneable接口
该接口为标记接口,实现该接口才能实现克隆方法,否则程序会抛出异常。(克隆的前提:被克隆的对象必须实现Cloneable接口,必须重写clone方法)
public Object clone() {
try {
// 当用Object方法里面的clone方法,AbstractList类中是没有clone方法的,该方法的实现是由C
// 写的
ArrayList<?> v = (ArrayList<?>) super.clone();
// 通过调用Arrays.copyOf方法进行数据的copy
v.elementData = Arrays.copyOf(elementData, size);
// 指定新的ArrayList集合的修改次数为0
v.modCount = 0;
// 返回新集合
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
// ---------------------------Arrays类中--------------------------------
@SuppressWarnings("unchecked")
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) {
@SuppressWarnings("unchecked")
// 三元校验 用于校验是生产一个默认类型的数组还是一个指定类型的数组
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
// 将原数组中数据copy到新数组中并返回新数组
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
(注意:copy数组的内存容量与其原数组的元素个数相同)
克隆补充知识
浅拷贝和深拷贝
浅拷贝和深拷贝的区别在于拷贝对象中引用类型是否进行了copy。浅拷贝,深拷贝在对基础元素进行copy时效果相同,但在引用类型copy是浅拷贝拷贝的是索引,深拷贝则是copy引用对象后重新指定到新的引用对象。
RandomAccess接口
RandomAccess接口为标记接口,实现该接口的类随机遍历效率要高于顺序遍历,ArrayList实现了该接口。
// 随机访问
for (int i = 0; i < list.size(); i++) {
// 取出集合的每一个元素
list.get(i);
}
// 顺序访问
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
iterator.next();
}
在ArrayList中随机访问效率高于顺序访问,在LinkedList中顺序访问效率高于随机访问。