- LinkedList特点
- 双向链表实现,因此没有固定容量,不需要扩容
- 元素时有序的,输出顺序与输入顺序一致
- 允许元素为 null
- 所有指定位置的操作都是从头开始遍历进行的
- 和 ArrayList 一样,不是同步容器
- 需要更多的内存,LinkedList 每个节点中需要多存储前后节点的信息,占用空间更多些。
- 查找效率低,插入删除效率高。
- LinkedList结构
继承AbstractSequentialList并实现了List接口,Deque接口,Cloneable接口,Serializable接口,因此它支持队列操作,可复制,可序列化。
- 1
- 2
- 3
- LinkedList本质
LinkedList是个双向链表,它有三个成员变量。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
其中节点是一个双向节点
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- modCount变量[fail-fast机制]
类似ArrayList,LinkedList也维护了modCount变量,其记录了数组的修改次数,在LinkedList的所有涉及结构变化的方法中都增加modCount的值。
该变量迭代器等方面体现。
- 1
- 2
- 3
- 4
- 5
因此在使用迭代器迭代过程中,不允许对链表结构做修改(如插入新节点),否则会抛出异常 java.util.ConcurrentModificationException。
- indexOf(Object o)
该方法会根据是否为null使用不同方式判断。如果是元素为null,则直接比较地址,否则使用equals的方法比较,加快比较效率。lastIndexOf(Object o) 同理。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- clone() 浅拷贝
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
举个例子如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
从上述输出结果可以看出,尽管克隆的链表不是跟原链表同一块内存,但内容引用是同样的,指向同一个地址,可以从输出结果看出,因此改变克隆后链表的元素内容,相应的原链表内容发生相应变化,因此clone()方法是浅拷贝。
(注:对于基本类型,如int,则不会发生这种情况。)
- toArray() 和 toArray(T[] a)
跟上述clone()方法类似,同样也是浅拷贝。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- clear()
因为底层实现不是数组,LinkedList中的 clear方法稍微复杂一些,需要对每个节点的所有属性置null。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- node(int index)
该方法获得指定位置index的节点,其实现虽然也是遍历链表,但由于该链表是双向链表,因此支持双向查找。查找前会根据指定位置index判断是在链表的前半段还是后半段,从而决定是从前往后找或是从后往前找,提升查找效率。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- Queue 队列操作
由于LinkedList实现了Deque接口,而Deque继承了Queue,因此LinkedList也可以进行队列操作,包括:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- Deque 双向队列操作
由于LinkedList实现了Deque接口,因此可用于双向队列。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- listIterator(int index)
- 1
- 2
- 3
- 4
- 5
其中ListItr是LinkedList的一个内部类,实现了ListIterator接口,是个支持双向的迭代器。其实现细节较长,就不贴了,见上一节源码解析即可。
- descendingIterator()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
利用上面实现的双向迭代器类ListItr,可轻易得实现了逆向的迭代器。
- spliterator()
java8新增方法,类似Iiterator, 可以理解为 Iterator 的 Split 版本,可用于多线程,仍需学习。
【引用网上的说明】
使用 Iterator 的时候,我们可以顺序地遍历容器中的元素,使用 Spliterator 的时候,我们可以将元素分割成多份,分别交于不于的线程去遍历,以提高效率。使用 Spliterator 每次可以处理某个元素集合中的一个元素 — 不是从 Spliterator 中获取元素,而是使用 tryAdvance() 或 forEachRemaining() 方法对元素应用操作。
但 Spliterator 还可以用于估计其中保存的元素数量,而且还可以像细胞分裂一样变为一分为二。这些新增加的能力让流并行处理代码可以很方便地将工作分布到多个可用线程上完成。
- 同步问题
LinkedList 和 ArrayList 一样,不是同步容器。所以需要外部做同步操作,或者直接用 Collections.synchronizedList 方法包一下:
- 1
此时list是线程安全类,自身提供的方法也是线程安全的。当然list进行其他非原子操作仍需自己同步。