我们在学习这一块内容时需要注意的一个问题是 集合中存放的依然是对象的引用而不是对象本身。

List接口扩展了Collection并声明存储一系列元素的类集的特性。使用一个基于零的下标,元素可以通过它们在列表中的位置被插入和访问。一个列表可以包含重复元素。List在集合中是一个比较重要的知识点也是在开发中最常用的。

我们都知道ArrayList是由数组实现的,但是和数组有很大区别的是随着向ArrayList中不断添加元素,其容量也自动增长,而数组声明好之后其容量就不会改变。想要探明其中的究竟探析其中的原理十分重要,今天重新看了一下这块的源代码(JDK1.8.0_92)感觉很有收获,所以在此记录和分享。

1.Arraylist类中的属性

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 类属性

在这里需要注意的有几点:

DEFAULT_CAPACITY 这个变量指的是ArrayList默认的容量,其实刚刚初始化一个Arraylist其容量是0,当添加一个之后容量就变成了10,在jdk1.6版本的时候还不是这么处理的。接下来会一一介绍。

elementData 这个变量是一个数组,在JDK1.8.0_92的源代码的注解中很清晰的说明了这个数组是用来缓存ArrayList里的数据(这里的数据指的是对象的引用),ArrayList的大小取决于这个缓存数组的长度,还指明了一点就是在初始化的时候这个缓存数组的是一个空数组当第一次添加的时候会把这个缓存数组的长度扩展为上面的DEFAULT_CAPACITY也就是10。

size 这个变量就指的是缓存数组的大小也就是ArrayList的长度。

2.构造方法

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 View Code

在这里需要注意的是,在不同版本的jdk中此处的实现机制略有不同。如下:

在jdk1.8.0_45中不带参数的构造方法:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 View Code

在JDK1.6中不带参数的构造方法:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 View Code

在JDK1.8.0_92和jdk1.8.0_45中代码虽然略微有些不同但是他们的实现机制是一样的,都是刚开始的声明一个空数组是在添加的时候才把数组的长度扩展为10,而在JDK1.6时在刚刚声明的时候就声明的长度为10的数组。这也是慢慢在做优化吧。

具体是在什么时候数组长度进行扩展的我们在下边看

3.添加元素

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 View Code

上面几个添加方法中具体的实现方法没有在上边列出来我放在下边

具体的实现方法:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 View Code

在这里值得注意的是grow()方法中的>> 1,其实该运算就是相当于除以2.例如:

 1010      十进制:10     原始数         number
10100      十进制:20     左移一位       number = number << 1;
 1010      十进制:10     右移一位       number = number >> 1;

所以我们跟进 add(E e)方法就会知道当对ArrayList进行add操作时,最开始的时候进入add()方法,然后执行ensureCapacityInternal()方法,继续跟进在这里elementData == EMPTY_ELEMENTDATA为true所以minCapacity = 10。然后minCapacity - elementData.length > 0为true,执行grow()方法,每次执行grow()方法在一般情况下都会生成一个新数组且长度是原数组长度的1.5倍,但是有两种情况除外第一点就是当新生成的数组长度小于10时 那么将新生成的数组长度改为10,也就是grow()方法第一个if语句里的代码做的事情(就是保证了在添加元素小于8个的时候ArrayList的长度都是10),第二点就是当新生成的数组长度大于Integer.MAX_VALUE - 8时会对数组长度进行调整避免越界(这就是grow()方法第二个if语句里的代码做的事情)。这就是ArrayList容量自动扩充的基本原理。

我之前看过jdk1.6在此处的实现方式,略有不同但是也是生成新数组且长度是原数组的1.5倍。至于为什么是1.5倍我觉得可能是那些大牛以自己的经验或是经过科学的推导才得到这么一个最优解,我也不知道了 ,猜的 欢迎补充。

就介绍这么多吧,ArrayList里还有很多方法像删除元素,修改元素还有查找元素等等比较容易读懂,就不在这里都写出来了。

我们大致了解了ArrayList的基本原理然后再去想ArrayList的特点就比较容易懂了。相对于LinkedList,ArrayList比较适用于搜索操作,LinkedList比较适用于插入或是删除操作。因为ArrayList适用数组实现的,在内存中所存储的数据是连续的所以很容易从一个元素定位到另一个元素,而LinkedList就不同了,LinkedList适用双链表实现的他就必须挨个一个一个的看是不是要找的元素,但是LinkedList在执行中间插入或是中间删除操作时效率是很高的。

还有在eclipse中查看源代码比较常用的快捷键是Alt + 左右方向键,可以实现在之前鼠标光标处跳转 非常实用。

欢迎大家查补缺漏!