基于JDK1.8的ArrayList剖析
前言
本文是基于JDK1.8的ArrayList进行分析的。本文大概从以下几个方面来分析ArrayList这个数据结构
- 构造方法
- add方法
- 扩容
- remove方法
(一)构造方法
1 /** 2 * Constructs an empty list with the specified initial capacity. 3 * 4 * @param initialCapacity the initial capacity of the list 5 * @throws IllegalArgumentException if the specified initial capacity 6 * is negative 7 */ 8 public ArrayList(int initialCapacity) { 9 if (initialCapacity > 0) { 10 this.elementData = new Object[initialCapacity]; 11 } else if (initialCapacity == 0) { 12 this.elementData = EMPTY_ELEMENTDATA; 13 } else { 14 throw new IllegalArgumentException("Illegal Capacity: "+ 15 initialCapacity); 16 } 17 } 18 19 /** 20 * Constructs an empty list with an initial capacity of ten. 21 */ 22 public ArrayList() { 23 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 24 }
总所周知,ArrayList底层是数组
我们先看第二个构造方法,即无参构造方法(第22行),将默认空数组赋值给Object数组。这一段执行完,就在堆中分配了一段空的数组。
我们再看第一个构造方法,有参构造方法。
1 /** 2 * 加入我们有这么一段代码 3 */ 4 List<String> list = new ArrayList<>(10);
其构造方法有三个分支,initialCapacity大于0,等于0,小于0。我们直接看大于0,其他两个看代码就懂了。
他会直接构造一个以initialCapacity为大小的Object数组。
(二)add方法
1 /** 2 * Appends the specified element to the end of this list. 3 * 4 * @param e element to be appended to this list 5 * @return <tt>true</tt> (as specified by {@link Collection#add}) 6 */ 7 public boolean add(E e) { 8 ensureCapacityInternal(size + 1); // Increments modCount!! 9 elementData[size++] = e; 10 return true; 11 }
size这个字段为数组的长度。然后进入ensureCapacityInternal这个方法。
1 private void ensureCapacityInternal(int minCapacity) { 2 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 3 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 4 } 5 6 ensureExplicitCapacity(minCapacity); 7 }
可以看出来,第二行是判断是否位第一次add,也就是初始化。如果数组位空,那么DEFAULT_CAPACITY就是为10。
初始化完成,也就是开辟一个大小为10的Object数组。
1 private void ensureExplicitCapacity(int minCapacity) { 2 modCount++; 3 4 // overflow-conscious code 5 if (minCapacity - elementData.length > 0) 6 grow(minCapacity); 7 }
minCapacity是list里面元素的个数,比如第一次放,也就是10,elementData.length也就是数组的长度,也就是0。
那么减法结果就为 10,不成立就不扩容,如果成立就扩容。
1 elementData[size++] = e;
然后返回add方法,执行这一句,至此,add方法就执行完毕了.接下来我们看下扩容的代码。
(三)扩容
1 /** 2 * 我们假设第一次扩容,list数组容量为10,再次add那么传过来的minCapacity为11 3 * oldCapacity为10 4 * newCapacity为 10 + (10 / 2) = 15 也就是扩容1.5倍 5 * 有两个if的极值判断,看代码很简单就懂了 6 * 然后执行Arrays.copy方法 7 */ 8 private void grow(int minCapacity) { 9 // overflow-conscious code 10 int oldCapacity = elementData.length; 11 int newCapacity = oldCapacity + (oldCapacity >> 1); 12 if (newCapacity - minCapacity < 0) 13 newCapacity = minCapacity; 14 if (newCapacity - MAX_ARRAY_SIZE > 0) 15 newCapacity = hugeCapacity(minCapacity); 16 // minCapacity is usually close to size, so this is a win: 17 elementData = Arrays.copyOf(elementData, newCapacity); 18 }
我看了下Arryas的copy方法,也不难,大家可以对照着源码自己去看一下。
至此ArrayList的add方法就介绍完了,grow方法也搞定了。
(四)remove方法
1 /** 2 * remove方法总共有两个,一个是按照index删除,一个是按照元素删除,大同小异 3 * 下面说按照index删除,看注释 4 */ 5 public E remove(int index) { 6 //首先检查是否越界 7 rangeCheck(index); 8 9 modCount++; 10 E oldValue = elementData(index); 11 12 int numMoved = size - index - 1; 13 //把后面的全部移到前面一位,System.arraycopy是一个native方法 14 if (numMoved > 0) 15 System.arraycopy(elementData, index+1, elementData, index, 16 numMoved); 17 //然后最后一位置为空值,gc会自动回收 18 elementData[--size] = null; // clear to let GC do its work 19 20 return oldValue; 21 }
remove方法很简单,直接看我写的注释即可。
(五)总结
写了两个多小时,我也醉了。主要我在调试eclipse的debug,我的那个有点问题,eclipseEE版本就没有问题。
其实我有一点疑惑,希望大佬能够给我解答以下。
初始化构造方法那里,并没有给size赋值,也就是为null值,不能直接输出或者使用(会报错,已实验)
但调用list.size()方法的时候,返回的确是0,没有报错,我debug也没看出来什么时候赋值了。
大佬看到这篇文章,希望可以解答下。谢谢。
-------------------------------华丽分割线----------------------------
好累啊