LinkedList 源码剖析
文章目录
类图:

LinkedList 底层是双向链表

可以看到,LinkedList 实现了 Deque 接口,所以可以像操作队列和栈一样操作 LinkedList:

1. LinkedList 的属性
LinkedList 共有4个属性:

属性解析:

// 序列化版本UID
private static final long serialVersionUID = 876323262645176354L;
2. 构造方法
LinkedList 有一个空参构造器和一个有参构造器:

3. 添加方法 add()
add方法实际上就是往链表最后添加元素
public boolean add(E e) {
linkLast(e);//往链表尾端加元素的方法
return true;
}
void linkLast(E e) {
final Node<E> l = last;//先把尾元素赋给l
final Node<E> newNode = new Node<>(l, e, null);//调用内部类Node的有参构造器构造一个新结点
last = newNode;//再把新结点的引用赋给last
if (l == null)
first = newNode;//如果原本的尾元素为空,则将新结点赋给首元素,此时链表只有一个元素(首尾)
else
l.next = newNode;//否则将新元素赋给原尾元素的下一个位置
size++;//元素数量+1
modCount++;//链表修改次数+1
}
//内部类Node
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
//有参构造器
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
4. 移除方法 remove()

再来看看unlink()方法的内部逻辑:

上述代码进行的就是如下的操作:

5. 查找方法 get()
public E get(int index) {
checkElementIndex(index);//验证索引的合法性
return node(index).item;
}
//返回指定索引上的非空元素
Node<E> node(int index) {
// assert isElementIndex(index);能执行到node()方法,认定索引是合法的
if (index < (size >> 1)) {//用到类似二分查找思想,若index < size的一半,则从首元素开始找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {//若index ≥ size的一半,则从尾元素开始向前找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
6. set() 方法
set方法和get方法差不多,根据下标来判断是从头遍历还是从尾遍历
public E set(int index, E element) {
checkElementIndex(index);//验证索引合法性
Node<E> x = node(index);//查找元素
E oldVal = x.item;
x.item = element;
return oldVal;//将旧元素上的值返回
}
7. ArrayList 和 LinkedList 对比
- ArrayList 底层是数组,LinkedList 底层是双向链表
- 两者都是非同步的(线程不安全)
- 对比 ArrayList 和 LinkedList 的 API 的时间复杂度:
| 功能 | 方法 | ArrayList | LinkedList |
|---|---|---|---|
| 增 | add(E e) | O(1) | O(1) |
| 增 | add(int index, E e) | O(n) | O(n) |
| 删 | remove(int index) | O(n) | O(n) |
| 删 | remove(E e) | O(n) | O(n) |
| 改 | set(int index, E e) | O(1) | O(n) |
| 查 | get(int index) | O(1) | O(n) |
结论:
- 改查选择 ArrayList;
- 增删在尾部的选择 ArrayList,增删在中间位置也选择 ArrayList;
- 其他情况下,如果时间复杂度一样,推荐选择 ArrayList,因为 overhead 更小,或者说内存使用更有效率。
- 但一般来说,增删多还是用 LinkedList,因为上面的情况比较极端
本文深入剖析了LinkedList的属性、构造方法、add、remove和get/set操作,以及与ArrayList的性能对比,重点讲解了链表结构和其在增删查改中的优势与劣势。
1265

被折叠的 条评论
为什么被折叠?



