ArrayList是我们常用的集合之一,而我也会陆续对ArrayList集合的源码进行一步步的分析,由于本人的水平有限,难免有些理解上的偏差或错误希望读者能指出。
1,ArrayList集合中的属性
//序列化版本号
private static final long serialVersionUID = 8683452581122892189L; //默认初始化长度 private static final int DEFAULT_CAPACITY = 10; //空的elementData数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //默认的初始化空的elementData数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. * 存储ArrayList元素的数组缓冲区,ArrayList的容量是此数组缓冲区的长度。任何 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * 添加第一个元素时,将扩展为DEFAULT_CAPACITY
* 主数组 */ transient Object[] elementData; // non-private to simplify nested class access //ArrayList中保存的元素个数 可以当作一个计数器 private int size;
//数组的最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
以上便是ArrayList集合中的主要属性,标红的则是比较重要的属性,接下来我们看看ArrayList的构造方法
2,ArrayList的构造方法
1)无参数构造方法
//无参数的构造方法 public ArrayList() { //此时 elementData为{} this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
可以看到,当我们调用无参构造方法,创建ArrayList数组时(List list = new ArrayList();),此时ArrayList的底层数组为:{}
2)带参数构造方法
//传入一个初始化参数,对ArrayList数组进行初始化操作 public ArrayList(int initialCapacity) { if (initialCapacity > 0) {//如果传入参数大于0,则ArrayList数组的长度为传入的数值 this.elementData = new Object[initialCapacity];//此时便已经进行了初始化 } else if (initialCapacity == 0) {//如果等于零 this.elementData = EMPTY_ELEMENTDATA;//此时elementData数据为{} } else {//小于零抛出异常 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
可以看到,当我们调用有参构造方法创建ArrayList数组时候(List list = new ArrayList(3);),会对我们传入的参数进行判断,如果我们传入的参数大于0则此时对elementData数组进行初始化,初始化的
数组长度为传入的参数。如果我们传入的参数为0,则将EMPTY_ELEMENTDATA的值赋给elementDate数组为{}。如果传入值小于0,便会抛出相应的参数异常。
3)传入一个集合作为参数的构造方法
public static void main(String[] args) { List list = new ArrayList(); list.add(null); list.add(null); list.add("阿里爷爷"); list.add("阿里爷爷"); list.add("阿里爷爷"); List list2 = new ArrayList(list); System.out.println(list); }
//当传入一个集合作为数组初始化参数时 public ArrayList(Collection<? extends E> c) { //将传入的集合转换为一个Object类型的数组,并将此数组的引用赋给elementData elementData = c.toArray(); if ((size = elementData.length) != 0) {//数组不为空的情况 // c.toArray might (incorrectly) not return Object[] (see 6260652) //https://blog.csdn.net/qq_22083043/article/details/77513485 //Arrays.asList()返回一个内部类,调用内部类的toArray()方法返回的不是一个Object[],而是E[]。 //elementData.getClass() 一旦不是一个Object[]就会将他复制成为一个Object[] if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else {//数据为空的情况 // replace with empty array. this.elementData = EMPTY_ELEMENTDATA;//elementData为{} } }
我们可以看到,首先会通过集合的toArray方法将传入的集合转换为一个数组,然后将数组赋给elementData。然后将elementData.length赋给size,然后对size进行判定是否为0(即elementData数组中书否有元素)如果数组不为空,则会新进行elementData的类型进行判断,判定其是否为Object数组,如果不是便会将elementData数组复制成为一个Object类型的数组。如果为空的情况,则将elementData的数组赋值为{}。
对应if(elementData.getClass()!=Object[].class),我们可能会不明白,为什么这里需要判断,那么我么需要看看toArray()方法的底层了。
可以看到两个copyOf方法使用的都是,Arrays工具类中的copyOf()方法,然后我们查看Arrays的copyOf方法便了然了吧!!
后面笔者会陆续发表关于ArrayList中常用方法的源码解析,以及集合体系中的常用方法,java.util包中的常用方法,总之就是一系列java相关源码。
最后大家选择这条路,希望你我都能坚持下去。