![Java修炼指南:高频源码解析](https://wfqqreader-1252317822.image.myqcloud.com/cover/463/37323463/b_37323463.jpg)
2.2 List集合的一种典型实现——ArrayList类
ArrayList就是动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处:动态地增加和减少元素,实现了ICollection和IList接口以及灵活地设置数组的大小。在面试时候通常会被问到:数组和ArrayList的区别是什么?ArrayList的底层是什么?ArrayList线程是否安全,为什么?
关于这些问题,熟悉源码后就迎刃而解了。本节将重点介绍ArrayList类是如何实现的。
2.2.1 ArrayList定义
ArrayList是一个用数组实现的集合,支持随机访问,元素有序且可以重复。ArrayList类结构如图2-2所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/68_02.jpg?sign=1739703189-LWlHpfoMeQStiOnqXIwq2jrHCMSWLdWU-0-6a9bbcd7308722ad480dc66853cb4dcd)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/69_01.jpg?sign=1739703189-HL282KZi3UJBFnmypzTk1RJYTJffPHLY-0-e7cfc4c8da843d32cc4f9321c99ae832)
●图2-2 ArrayList类结构图
1. 实现RandomAccess接口
这是一个标记接口,一般此标记接口用于List实现,以表明它们支持快速(通常是恒定时间)的随机访问。该接口的主要目的是允许通用算法改变其行为,以便在应用于随机或顺序访问列表时提供良好的性能。
比如在工具类Collections(这个工具类后面会详细讲解)中,应用二分查找方法可以判断是否实现了RandomAccess接口:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/69_02.jpg?sign=1739703189-sAk0S43fBcbOX2JSo1e6RCZ83plQoOM9-0-0032b0b34e662a7ca2db40dd9109c647)
2. 实现Cloneable接口
这个类是Java. lang. Cloneable,学习Java深拷贝和浅拷贝原理时,知道浅拷贝可以通过调用Object. clone()方法来实现,但是调用该方法的对象必须要实现Cloneable接口,否则会抛出CloneNoSupportException异常。
Cloneable和RandomAccess接口也是一个标记接口,接口内无任何方法体和常量的声明,如果想克隆对象,必须要实现Cloneable接口,表明该类是可以被克隆的。
3. 实现Serializable接口
该接口也是标记接口,表示能被序列化。
4. 实现List接口
这个接口是List类集合的上层接口,定义了实现该接口的类都必须要实现的一组方法,如下所示,下面对这一系列方法的实现做详细介绍。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/70_01.jpg?sign=1739703189-zPf1AlKj3ygx0ziuNbeu62rLO3Z9cyVw-0-92ac3a93908077d94f42c48fc8176206)
2.2.2 字段属性
字段属性代码如下:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/70_02.jpg?sign=1739703189-GHtuzcsuIltLrtWNEKNNHQXOMsfQY1Vp-0-eeed14ce9a8f0e7a4f4b14ceecff61b2)
2.2.3 构造函数
构造函数代码如下:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/70_03.jpg?sign=1739703189-ON0mDwrVSGhNLyBdnrIT6YusWf5x1xho-0-407413f95f3f6db525ba1b954fd70501)
此无参构造函数将创建一个DEFAULTCAPACITY_EMPTY_ELEMENTDATA声明的数组,注意此时初始容量是0,而不是很多人认为的10。
注意:
根据默认构造函数创建的集合,ArrayList list = new ArrayList();此时集合长度是0。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/71_01.jpg?sign=1739703189-Ac1nzOjUSUusv32fMbeXELjOgKQf3No2-0-d79a75d39bf2f4ca536eef46beaa7d79)
初始化集合大小创建ArrayList集合。当大于0时,给定多少就创建多大的数组;当等于0时,创建一个空数组;当小于0时,抛出异常。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/71_02.jpg?sign=1739703189-t8tTP2urIAXOqcmKwU6jt0iu9DYWzYyr-0-12ad0f02a07350f563cb0d3825b00436)
以上代码即将已有的集合复制到ArrayList。
2.2.4 添加元素
通过前面的字段属性和构造函数,可以看出ArrayList集合是由数组构成的,那么向ArrayList中添加元素,也就是向数组赋值。众所周知一个数组的声明是能确定大小的,而使用ArrayList时,需要能添加任意多个元素,这就涉及数组的扩容。
扩容的核心方法就是调用Arrays. copyOf方法,来创建一个更大的数组,然后将原数组元素拷贝过去即可。下面看具体实现:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/71_03.jpg?sign=1739703189-kiS36FETF93RJQjwrMPhxwoEXmAzWpGZ-0-873c94d057002c9551c94c3139e097fa)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/72_01.jpg?sign=1739703189-92pKNAbd2sSXJkyUm19mA9DA1OnSolJL-0-d61ebd892459d413ae3ee46fb8b88cac)
如上所示,在通过调用add方法添加元素之前,要首先调用ensureCapacityInternal方法来确定集合的大小,如果集合满了,则要进行扩容操作。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/72_02.jpg?sign=1739703189-hpt3avy9AmedrIhsuCVrfLdBQPWhuYUg-0-c7d5f17e1ce724f2f4407130ed68aefa)
在ensureExplicitCapacity方法中,首先对修改次数modCount加一,这里的modCount给ArrayList的迭代器使用,在并发操作被修改时,提供快速失败行为(保证modCount在迭代期间不变,否则抛出ConcurrentModificationException异常),可以查看源码865行,接着判断minCapacity是否大于当前ArrayList内部数组长度,大于则调用grow方法对内部数组elementData扩容,grow方法代码如下:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/72_03.jpg?sign=1739703189-urWp489UzYwxxVoBLdDrpPPef5TDOO2N-0-687c2f131ed0809cdc796d6b87e2aba0)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/73_01.jpg?sign=1739703189-COXu5bAtbdFyaCIQrGL8H2APkhxI6aQy-0-5b4064c118da40531db0d030cbce93d3)
对于ArrayList集合添加元素,总结如下:
1)当通过ArrayList()构造一个空集合,初始长度是为0的,第1次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
2)第2次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。
3)第11次添加元素,此时size+1 = 11,而数组长度是10,这时候创建一个长度为10+10∗0. 5 = 15的数组(扩容1. 5倍),然后将原数组元素引用拷贝到新数组。并将第 11次添加的元素赋值到新数组下标为10的位置。
4)第Integer. MAX_VALUE - 8 = 2147483639,然后 2147483639%1. 5=1431655759(这个数是要进行扩容)次添加元素,为了防止溢出,此时会直接创建一个 1431655759+1大小的数组,这样一直,每次添加一个元素,都只扩大一个范围。
5)第Integer. MAX_VALUE - 7次添加元素时,创建一个大小为Integer. MAX_VALUE的数组,在进行元素添加。
6)第Integer. MAX_VALUE + 1次添加元素时,抛出OutOfMemoryError异常。
注意:
可以向集合中添加null,因为数组可以有null值存在。
2.2.5 删除元素
1. 根据索引删除元素
remove(int index)方法表示删除索引index处的元素,首先通过rangeCheck(index)方法判断给定索引的范围,超过集合大小则抛出异常;接着通过System. arraycopy方法对数组进行自身拷贝。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/73_02.jpg?sign=1739703189-Bni3T2NXM5rMswNyv1OJmxxawQ81Nuei-0-398d6d5557f4da62979fb0c56ad5b297)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/74_01.jpg?sign=1739703189-e102Xjcp94YsQIKZ1cnm7OasbddTuxA6-0-81a7e58c5f293354ca21e7a541603e9d)
2. 直接删除指定元素
remove(Object o)方法是删除第一次出现的该元素。然后通过System. arraycopy进行数组自身拷贝。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/74_02.jpg?sign=1739703189-ECX6E6bfu31v4WvZQpstFjULbKV47dsx-0-fdfa6c13f71f87eca7919cd805fa9a04)
2.2.6 修改元素
通过调用set(int index,E element)方法将指定索引index处的元素替换为element,并返回原数组的元素。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/74_03.jpg?sign=1739703189-QiRm0ZRlAN0d3BCytbo6JjoW652mXBeg-0-13d2ed3a8f9591f383d895bd3c60851e)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/75_01.jpg?sign=1739703189-EtWVhV9FpEIRzbqHadNHpgwbya8iBzm7-0-48b8e74f9454b82b761901e6c6b0fff8)
2.2.7 查找元素
1. 根据索引查找元素
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/75_02.jpg?sign=1739703189-y8bmZtFlXb4P1XGJwcsElgKrWXdFkTTq-0-6bc588da523abbffd27c87f746f7509f)
同理,该方法首先还是判断给定索引的合理性,然后直接返回处于该下标位置的数组元素。
2. 根据元素查找索引
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/75_03.jpg?sign=1739703189-Xf0ibaQO9P3pnSsw0rpx1fwGEOVbm6mH-0-7c0f8aa21af54ed5e0d395219a1e9597)
注意:
indexOf(Object o)方法是返回第一次出现该元素的下标,如果没有则返回 -1。
还有lastIndexOf(Object o)方法是返回最后一次出现该元素的下标。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/75_04.jpg?sign=1739703189-CdvieLjvukt1Zg5f4RpHD9S0pHQDysfJ-0-c3a111adc52f22c4df676bc55865f27f)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/76_01.jpg?sign=1739703189-aioRp7sffqfyyHxuNqFZVDnPwZREmlcZ-0-57d315fc8ec0cf4efceac756790ba5e7)
2.2.8 遍历集合
1. 普通for循环遍历
前面介绍查找元素时,可以通过get(int index)方法,根据索引查找元素,普通for循环遍历同理。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/76_02.jpg?sign=1739703189-tpNyHgEZuCNyhoS6Gb7wJj9zKqNktqSh-0-032d509b0cdb8f0e02a6a84d83e8fc85)
2. 迭代器iterator
先看看ArrayList的具体用法,代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/76_03.jpg?sign=1739703189-SRM9awm8LsQqKDjOzDe6Cwq0llw3OSWR-0-efbe43870ac286b1bcae9b97fbb09d53)
在介绍ArrayList时,该类实现了List接口,而List接口又继承了Collection接口,Collection接口又继承了Iterable接口,该接口有个Iterator<T>iterator()方法,能获取Iterator对象,并能用该对象进行集合遍历,为什么能用该对象进行集合遍历?先看看ArrayList类中的返回方法,代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/76_04.jpg?sign=1739703189-4k4uol3UMx4FfG5T8jWPalK4mzSraOpH-0-848cf2c54ed961837de8d834ef8ddd58)
该方法是返回一个Itr对象,这个类是ArrayList的内部类。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/77_01.jpg?sign=1739703189-Fs6OyCJYJK7a8gMEIdmOumxOPqFELhR5-0-1720b0698f052e4c44627c4f8ec2f535)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/78_01.jpg?sign=1739703189-qbJBEGPLcPtpPWsJPECYola8QUOEguJ0-0-290dcf481956f500ba122df92d10a34a)
注意:
在进行next()方法调用的时候,会进行checkForComodification()调用,该方法表示迭代器进行元素迭代时,如果同时进行增加和删除操作,会抛出ConcurrentModification Exception异常。比如:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/78_02.jpg?sign=1739703189-kFD1fxgdbH3efERIcoFnsIVuhBmrM6uC-0-2bc3a63f53116c0fec3aa55a1a343966)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/79_01.jpg?sign=1739703189-V5g7kkFxPTUlBuAzkRjJMs9vrFs8y9FX-0-17803e00d34447a1a874095f4e4eaf9c)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/79_02.jpg?sign=1739703189-mOxt5AEIy3rMKQdn2SKBZI6Tcnb0Mn2w-0-3bb2c200cff2e7291cfc5123220fff7b)
解决办法是不调用ArrayList. remove()方法,转而调用迭代器的remove()方法。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/79_03.jpg?sign=1739703189-bpOJpDRMBnS2uLMIMCPQ9iDQFo6Z86ZR-0-cd6dc54cdf8a7c21a2accc543183539b)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/80_01.jpg?sign=1739703189-7ASbJl1lovthCyJwsUztrXwIeTDb88rU-0-6ea2edc96620e11c87f6a4d5a9ac423a)
这种语法可以看成是JDK的一种语法糖,通过反编译class文件,可以看到生成的Java文件,其具体实现还是通过调用Iterator迭代器进行遍历。示例代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/80_02.jpg?sign=1739703189-AiN3xeju18q7h88LkXgEidnIUEVyf4EQ-0-05eaf7be41859b8a0d419cb6c97b5f35)
3. 迭代器ListIterator
还是先看看ArrayList具体用法,示例代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/80_03.jpg?sign=1739703189-QvFWAu1nhUftqTtil3DKg6zIKJA1IbaP-0-806abaaf7d72186d32ff216a768568b1)
ArrayList还能实现一边遍历,一边进行新增或者删除操作:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/80_04.jpg?sign=1739703189-KEubb9X7CmYoSYrIH1gyqAf27NL0hPJE-0-7aaeaa05029013cde45f42170a3270cd)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/81_01.jpg?sign=1739703189-useDoTFjus8JkrCMDoquMIkn9iztG6t6-0-7fa1edbbf24508161b8e7c84ce2b0af2)
也就是说相比于Iterator迭代器,这里的ListIterator多出了能向前迭代以及能够新增元素。示例代码如下所示。
对于Iterator迭代器,查看JDK源码,发现还有ListIterator接口继承了Iterator。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/81_02.jpg?sign=1739703189-Yb4kuK83e354DGbJHDPEzOFFN6sqdHyH-0-6fe45db4d22703f6f8420acb8bdd314d)
该接口有如下方法:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/81_03.jpg?sign=1739703189-geax2P1HcwECmhUxjzUt6TEuDnxUSVem-0-61dc15876145a5a4a4cde830275dc3e7)
在ArrayList类中,有如下方法可以获得ListIterator接口。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/81_04.jpg?sign=1739703189-k8YSZpbtuebsx8gWGVZX1gAUutxfboie-0-667dcd1dd53a7a4d81f8a49b732129ba)
这里的ListItr也是一个内部类。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/81_05.jpg?sign=1739703189-6LkzLwHyKOW7iwuc4eCttPdiro3HphDn-0-bea5ffe42f66379ce2e9e95910791903)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/82_01.jpg?sign=1739703189-wTok5JD7E9afYGJEdyz78ZBAB62owAUg-0-2f3b7f57291c1b665823250f1be2a4ae)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/83_01.jpg?sign=1739703189-UcCJ8U3ayuoo7k5W7qIF2E0TjpstZN4V-0-e935188b13f3f45233b00e3601f62484)
2.2.9 SubList方法
在ArrayList中有这样一个方法:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/83_02.jpg?sign=1739703189-pr5oWJiSnNZcdkpZFfMAUeJgLgifQLTw-0-791437222446a358dc9ea9193734e416)
该方法作用是返回从fromIndex(包括)开始的下标,到toIndex(不包括)结束的下标之间的元素视图。示例代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/83_03.jpg?sign=1739703189-rh38mmKF9H57GKdCNqWxafO8XxhHf2Qj-0-960207d71f0db89e683f6c9d2c8b7152)
这里出现了SubList类,这也是ArrayList中的一个内部类。
注意:
返回的是原集合的视图,也就是说,如果对SubList类中出来的集合进行修改或新增操作,那么原始集合也会发生同样的操作。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/83_04.jpg?sign=1739703189-cSC47xo08hYKxLWlrqNTH9aJmUykWWSi-0-e21807454f61bfab00549466a0bbad6e)
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/84_01.jpg?sign=1739703189-F2KAM9bvVI8qGpn2sxz5bUu1GD1Gg2yx-0-8c488ba39ca9b10a01a0d9058aec3b46)
如想要独立出来一个集合,解决办法如下:
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/84_02.jpg?sign=1739703189-HipNoorWgKUiyMBhSlLteZySz6u09bgb-0-a860b3164381163d76b7e15f4530908d)
2.2.10 size()方法
通过size()方法返回集合的长度,一般是指元素的数量,代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/84_03.jpg?sign=1739703189-3BMmS6lnUTzYlm3QW3opG1W7OoWFQQQt-0-880a4d699b15dfb2000d9f873277d8e4)
注意:
返回集合的长度,而不是数组的长度,这里的size就是定义的全局变量。
2.2.11 isEmpty()方法
这个方法是判断集合是否为空,代码如下所示。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/84_04.jpg?sign=1739703189-yo1T9VeBsKyih0r4aHw9cD7Dz2ZHdDWe-0-d05c5cc229d727ecb8379a96e875704a)
返回size == 0的结果。
2.2.12 trimToSize()方法
该方法用于回收多余的内存。即一旦确定集合不在添加多余的元素之后,调用trimToSize()方法会将实现集合的数组大小刚好调整为集合元素的大小。
![](https://epubservercos.yuewen.com/BCE792/19773740801350006/epubprivate/OEBPS/Images/85_01.jpg?sign=1739703189-P2LsOAJkLxfqKsUxfXpwpOkMp9joApFY-0-ad2bf4db7a0c7fa7f6a350ec26178f17)
注意:
该方法会花时间来复制数组元素,所以应该在确定不会添加元素之后再调用。