基于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也没看出来什么时候赋值了。

大佬看到这篇文章,希望可以解答下。谢谢。

-------------------------------华丽分割线----------------------------

好累啊

posted @ 2018-02-24 16:18  程序员博博  阅读(315)  评论(0编辑  收藏  举报