环境准备
LinkedList基本介绍
- LinkedList底层实现了双向链表和双端队列特点;
- 可以添加任意元素(元素可以重复),包括null;
- 线程不安全,没有实现同步;
LinkedList的底层操作机制
- LinkedList底层维护了一个链表;
- LinkedList中维护了两个属性first和last,分别指向首节点与尾节点;
- 每个节点
Node里面维护了prev、next、item三个属性,最终实现双向链表;- prev: 指向前一个节点;
- next: 指向后一个节点;
- 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率高一些;
实现一个双向链表
实现思路
代码实现
@SuppressWarnings(value = "all")
public class LinkedList01 {
public static void main(String[] args) {
// 模拟一个双向链表
// mock 节点数据
Node zS = new Node("张三");
Node lS = new Node("李四");
Node wW = new Node("王五");
// 链接三个节点 实现双向链表
// z => l => w
zS.next = lS;
lS.next = wW;
// w => l => z
wW.prev = lS;
lS.prev = zS;
// 让双向链表的首节点指向 张三
Node first = zS;
// 让双向链表的尾节点指向 王五
Node last = wW;
// 从头部开始遍历
while (first != null){
System.out.println(first);
first = first.next;
}
System.out.println("===== ***** =====");
// 从尾部开始遍历
while (last != null){
System.out.println(last);
last = last.prev;
}
}
}
/**
* 定义一个Node类
* 表示双向链表的一个节点
*/
class Node {
/**
* 存放的数据
*/
Object item;
/**
* 指向下一个节点
*/
Node next;
/**
* 指向上一个节点
*/
Node prev;
public Node(Object item) {
this.item = item;
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
'}';
}
}
输出
Node{item=张三}
Node{item=李四}
Node{item=王五}
===== ***** =====
Node{item=王五}
Node{item=李四}
Node{item=张三}
LinkedList源码分析
分析实例代码
public class LinkedListSource {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(100);
list.add(100);
}
}
分析源码-增add
1.加断点,F7 Step into
2.只有一个默认的无参构造,创建了LinkedList对象
这时list的两个关键属性first和last都为null
3.插入数据add,F7 Step Into
4.linkLast插入的核心函数,从名字上可以看出链接再后面
- 第一次添加的时候,创建一个新节点 last和first都塞入
5.完成第一次添加,查看数组元素,first和last都指向同一个内存地址!
6.再次添加一位元素,查看linkLast函数如何执行,F7 Step Into
7.last节点有值,意味l有值,所以修改l的next的指向为新节点
7.1 newNode的prev在哪儿指向的?这里先放一个疑问!
8.查看数据
linkLast
这里会解决上面的一个疑问:newNode的prev在哪儿指向的?
linkLast源码
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
- 第一次添加的时候,first和last都指向同一个;
- 第二次添加的时候,原节点的next指向新插入的节点,新节点的prev指向上一个插入的节点;
揭开疑问
其实如果再
final Node<E> newNode = new Node<>(l, e, null);这一步F7追进去就明白了
final Node<E> newNode = new Node<>(l, e, null);
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
l:上一个节点
e:传入的数据
(this.prev = prev) = (this.prev = l)