java集合类源码详解-ArrayList(2)-基于JDK8

上次关于ArrayList的结构没有做总结。这次还是补充在自己博客里面吧。

ArrayList继承自一个抽象类。实现了四个接口。

AbstractList继承自AbstractCollection。AbstractCollection继承自Object。

ArrayList底层结构是数组。所以其特点就是,查询(随机访问)快,增删慢(因为每删掉或者增加数组中的一位或者多位,这个位置后面的数都会往前移),效率高。

那为什么ArrayList要实现这个四个接口呢?

List:这个就不用说。

RandomAccess:实现这个接口只要是为了拥有随机访问功能。

Serializable:为了使其支持序列化。

Cloneable:为了覆盖函数clone(),即支持克隆。

关于Cloneable。来自JDK1.8 google。的解释:

//一个类实现Cloneable接口,以指示Object.clone()方法,该方法对于该类的实例进行现场复制是合法的。
//不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException 。
//按照惯例,实现此接口的类应使用公共方法覆盖Object.clone (受保护)。
// 有关覆盖此方法的详细信息,请参阅Object.clone() 。
//注意,此接口不包含clone方法

这里要注意,这里虽然实现了Cloneable和RandomAccess这两个接口,但是当你进入这两个接口查看的时候,会发现这两个接口里面什么都没有。

ArrayList里面有很多的方法,这次测试三个,上次测试的无参构造和add()。这次测试指定容量的有参构造和add()和trimToSize()。

点击下一步,会跳转到静态变量那里,这里就略过,上篇博客已经记录了。

由于我们指定了默认参数,所以肯定会跳转到有参构造里面来。在执行ArrayList的无参构造时,会先执行ArrayList的父类

AbstractList的无参构造并且初始化modCount。

点击下一步,然后会回到ArrayList的有参构造继续执行。此时会判断用户指定的初始容量是否是大于0

如果是大于0,那么就new一个object类型的数组。赋给elementData这个数组。elementData上章博客已经解释了,这里稍微再提一次。

elementData:transient Object[] elementData;
ArrayList基于数组实现,用该数组保存数据, ArrayList 的容量就是该数组的长度

如果这个初始化大小等于0,就会把EMPTY_ELEMENTDATA这个数组赋给elementData。

如果上面的两个条件都不满足,就会抛出异常-----非法参数异常

这里程序肯定是走第一个if,因为我们指定的初始化容量是5,5显然是大于0的。

此时有参构造执行结束,点击下一步,回到我们自己的代码,开始执行add。size是数组的实际长度,这个时候还没有添加任何元素,所以此时的size是0。

点击下一步执行ensureCapacityInternal()。这个函数就是对minCapacity进行确认。这里可以看到minCapacity这个变量的值是1.这里会进行一个逻辑判断是否element==DEFAULT_EMPTY_ELEMENTDATA。其实就是看这个容器是不是默认初始化,也就是判断是未指定初始容量。

这里我们指定了初始容量,所以肯定是不会执行这个if,而是准备开始执行ensureExplicitInternal()这个函数。

点击下一步。此时最重要的一部就来了。判断最小容量是否大于数据的当前容量。从下面的调试信息来看。

现在的最小容量是1.数组当前容量我们指定了是5。我们现在的最小容量还没有超出数组的可容纳范围即数组当前容量。

所以if判断肯定是false即我们现在不需要对数组进行扩容。

点击下一步,开始弹栈。开始把值添加进数组里面。添加完后size+1。size的大小变成1。也就是现在的数组的实际大小是1。

这里为什么调用ensureCapacityInternal()传的值是size+1呢? 

因为add()方法,添加数据是一个一个的添加,所以当我们数组里面没有值,然后我们想添加值的时候,(此时size=0)是不是得至少保证数组的最小容量是1。才能添加啊。依次类推,当数组大小是5,7,6等等其他大小的时候,我们想添加数据,是不是都得保证此时的最小容量Capacity都得比之前至少要大1。

这里开始测试trimToSize()方法。我们此时这个例子的数组容量。elementData.length是5。size是1。可以说数组容量(或者说数组缓冲区)是远大于数组容纳的数的数量。这会造成空间的浪费。再确定不再往容器里面添加数据的时候才调用这个方法。

这里我们再次点击下一步。这里因为会对ArrayList对象的容量进行操作,所以会触发modCount++。

再次点击下一步,此时会执行一个判断。数组实际的数据长度是否小于数据的容量。

这里我们当然是True,从下面可以看出,一个是1,一个是5。

 再次点击下一步,开始执行一个三目运算。

elementData = (size == 0)
        ? EMPTY_ELEMENTDATA
        : Arrays.copyOf(elementData, size);

这句的执行是先判断size是否为0,如果为0则返回True,将EMPTY_ELEMENTDATA(空数组)赋给elementData。

这里我们显然是False。所以这里会执行Arrays.copyOf(elementData, size)。将elementData这个数组的长度变为size长度。

并且将新数组返回给elementData。

 再点击下一步。

后续楼主会继续学习集合类的源码,另外博客什么地方写错,请指出,大家一起学习进步!! 


 

猜你喜欢

转载自blog.csdn.net/qq_37889257/article/details/84106538
今日推荐