ArrayList
1 | public class ArrayList<E> extends AbstractList<E> |
- ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长
- 允许null
- ArrayList 实现了RandmoAccess接口,即提供了随机访问功能
- ArrayList中的操作不是线程安全的,通过Collections.synchronizedList(new ArrayList(…))包装,或者使用CopyOnWriteArrayList,Vector
- 初始容量DEFAULT_CAPACITY = 10
- 遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!for循环遍历居中
数据结构
ArrayList包含了两个重要的对象:elementData 和 size。
1 | transient Object[] elementData; |
- elementData 是”Object[]类型的数组”,它保存了添加到ArrayList中的元素。elementData是个动态数组,大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,由ensureCapacity()函数控制。
1 | // jdk1.8 |
- size 则是动态数组的实际大小。
源码
- add函数
1 | public boolean add(E e) { // 添加元素 |
ensureCapacityInternal()确保elementData数组长度足够
1 | private void ensureCapacityInternal(int minCapacity) { |
grow函数对数组进行扩容
1 | private void grow(int minCapacity) { |
- remove(int index)
1 | public E remove(int index) { |
- clear()
1 | public void clear() { |
- clone()
1 | public Object clone() { |
- 序列化
1 | //java.io.Serializable的写入函数 |
1 | // java.io.Serializable的读取函数:根据写入方式读出 |
fail-fast 机制
- Iterator在调用 next() 和 remove()时,都会执行 checkForComodification()。若 “modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。
- 无论是add()、remove(),还是clear(),只要涉及到修改集合中的元素个数时,都会改变modCount的值
- 通过CopyOnWriteArrayList解决线程安全:
1 | private COWIterator(Object[] elements, int initialCursor) { |
可以看出:
和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。
ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。
ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!
LinkedList
1 | public class LinkedList<E> |
- LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
- LinkedList 实现 List 接口,能对它进行队列操作。
- LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。既然是双向链表, 顺序访问会非常高效,而随机访问效率比较低
- LinkedList支持序列化,能通过序列化去传输。
- LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
- 允许空元素,非同步,Collections.synchronizedList包装
数据结构
1 | // jdk1.8 |
源码
- 双向链表和索引值的联系
实际原理非常简单,它就是通过一个计数索引值来实现的。
例如,当我们调用get(int location)时,首先会比较“location”和“双向链表长度的1/2”;若前者大,则从链表头开始往后查找,直到location位置;否则,从链表末尾开始先前查找,直到location位置
- add()
对于添加一个元素至链表中会调用add方法 -> linkLast方法。
1 | public boolean add(E e) { |
- addAll()
1 | // 添加一个集合 |
说明:参数中的index表示在索引下标为index的结点(实际上是第index + 1个结点)的前面插入。在addAll函数中,addAll函数中还会调用到node函数,get函数也会调用到node函数,此函数是根据索引下标找到该结点并返回,具体代码如下
1 | Node<E> node(int index) { |
- unlink(Node
x)
在调用remove移除结点时,会调用到unlink函数,unlink函数具体如下:
1 | E unlink(Node<E> x) { |
- 序列化
1 | // 将LinkedList的“容量,所有的元素值”都写入到输出流中 |
1 | // 先将LinkedList的“容量”读出,然后将“所有的元素值”读出 |
总结
ArrayList
是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。LinkedList
是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList
随机访问效率低,但随机插入、随机删除效率低。