ArrayList源码分析(一)
这是Java源码分析的第一章的第一部分 万事开头难,因此开头写详细一点,后面熟练以后可能会简写。
类定义
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
常量
serialVersionUID
private static final long serialVersionUID = 8683452581122892189L;
实现Serializable接口的目的是为类可持久化,比如在网络传输或本地存储,为系统的分布和异构部署提供先决条件。若没有序列化,现在我们所熟悉的远程调用,对象数据库都不可能存在,类的序列化是通过serialVersionUID 实现的。
DEFAULT_CAPACITY
private static final int DEFAULT_CAPACITY = 10;
默认容量是10,ArrayList刚创建时,容量为0,一旦加入第一个元素时,容量就变为10,当容量满后会自动扩容,每次扩容大小为原来的1.5倍(后文会分析到)。
EMPTY_ELEMENTDATA & DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
elementData
transient Object[] elementData;
elementData是我们的核心数组,即存储和扩容的对象,因此ArrayList的底层是基于数组实现的 所以它访问查询快,增删慢,注意elementData是并不是私有的,源码给出解释是为了简化嵌套类的调用。
size
private int size;
ArrayList的大小
构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
这个构造方法可以指定初始容量
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这个无参构造,默认容量为10
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
这个构造函数,参数是一个特定的集合Collection<? extends E> c,elementData = c.toArray();先把c变成数组赋值给核心数组。
但是这里要对elementData 的类型进行判断。看它是不是Object类型。
看下面两行代码:
System.out.println(Object.class);
System.out.println(Object[].class);
输出结果为:
class java.lang.Object
class [Ljava.lang.Object;
这是属于Object类型,
ArrayList<String> list = new ArrayList<>(5);
System.out.println(list.getClass());
System.out.println(list.toArray().getClass());
输出结果为:
class java.util.ArrayList
class [Ljava.lang.Object;
因为toArray()方法返回的是Object[]类型,属于Object类
源码中的注释给出toArray()方法不一定返回Object[]类型
// c.toArray might (incorrectly) not return Object[] (see 6260652)
举例看这行代码:
System.out.println(Arrays.asList("abc").toArray().getClass());
输出结果为:
class [Ljava.lang.String;
发现是String[]类 并不是Object[]类
这样最终会导致ArrayList类中 elementData 更新元素时 无法添加Object类型的元素,要避免这种情况,判断:elementData.getClass() != Object[].class,不行的话调用Arrays.copyOf方法来实现把类型变为Object。
例子说明:
ArrayList<String> list = new ArrayList<>();
Object o = new Object();
list.add(o);
报错:
java: 对于add(java.lang.Object), 找不到合适的方法
方法 java.util.Collection.add(java.lang.String)不适用
(参数不匹配; java.lang.Object无法转换为java.lang.String)
父类转化不成子类,加强转也不行
ArrayList<String> list = new ArrayList<>();
Object o = new Object();
list.add( (String) o);
报错 java.lang.Object cannot be cast to java.lang.String
反之 父类是可以转化为子类的 下面代码就没有问题。
ArrayList<Object> list = new ArrayList<>();
String s = new String();
list.add(s);