持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第十二天,点击查看活动详情
Java中LinkedList详解(附带源码分析)
基于JDK 1.8源码分析
前言
我们在上一篇中了解到了ArrayList,作为ArrayList的同门师兄弟的LinkedList,他们有什么区别呢?我们先看一下LinkedList的结构图
我们通过这个结构图,我们简单介绍一下,每个接口和类的作用
- Collection接口:
Collection接口是所有集合的根节点,它定义了一组允许重复的对象 - List接口:
List是继承与Collection接口,List接口的集合类中的元素是对象有序且可重复 - AbstractCollection抽象类:主要是实现了
Collection接口的contains()、toArray()、removeAll()、toString() - AbstractList抽象类:主要实现了
List接口的方法 - Cloneable接口:它的作用是使一个类的实例能够将自身拷贝到另一个新的实例中
- Serializable接口:表示可以序列化和反序列化
- RandomAccess接口:该接口表示可以支持快速随机访问
- Deque 接口: 用来实现一个双端队列
- AbstractSequentialList类: 是
LinkedList的父类,是List接口的简化版实现
LinkedList实现原理
LinkedList底层内部实现不是数组,而是双向链表
基本属性
//长度
transient int size = 0;
//指向上一个节点
transient Node<E> first;
//指向下一个节点
transient Node<E> last;
transient关键字是防止属性被序列化,Node是LinkedList的内部类,我们来看一下源码:
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;
}
}
结构如下:
对于fitst来说没有上一个节点所有prev为null,对于last来说没有下一个节点,所以prev为null
初始化
LinkedList的构造函数比ArrayList少了一个,只有两个构造方法:
//空构造函数,什么也不做
public LinkedList() {
}
//将一个元素集合添加到LinkedList中
public LinkedList(Collection<? extends E> c) {
}
添加元素
add(E e)
通常我们使用add(E e)来添加元素,那我们看看源代码
public boolean add(E e) {
linkLast(e);
return true;
}
我们发现在add()中就只有两行代码,我们来看一下linkLast(e)的代码
void linkLast(E e) {
//这里的last就是链表的最后一个Node,如果是初次添加的话为null
final Node<E> l = last;
//创建一个新的Node,对于新Node来说之前的last就是上一个Node,last为null
final Node<E> newNode = new Node<>(l, e, null);
//上一个lastNode指向新Node
last = newNode;
//判断是否为初次添加,初次添加会把链头first赋值
if (l == null)
first = newNode;
else
//不是初次添加上一个Node的next指向newNode
l.next = newNode;
size++;
modCount++;
}
如果我们第一次添加的话,first和last都会指向newNode的结果,这时候还没有形成一个完整的双向链表,是一个断链的状态,结构就会如下图:
如果我们添加第二个元素后呢,它的结构就会如下图:
addFirst() 和 addLast()
我们除了可以调用add(E e)来实现添加,在LinkedList中天提供了addFirst() 和 addLast()方法
- addFirst():将元素添加到第一位
- addLast():将元素添加到末尾
我们我一下addFirst()的内部实现:
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
//获取到first
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
//给first赋值
first = newNode;
//判断是否第一次添加元素
if (f == null)
//第一次添加last也赋值给last
last = newNode;
else
//把之前的first的Node的prev指向新Node
f.prev = newNode;
size++;
modCount++;
}
addLast()和addFirst()也是类似,我在这里就不做过多的解释啦
删除
上面我们讲解了一下add()的原理,我们接下来讲解一下remove()的方法的源代码:
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
public boolean remove(Object o) {
//判断删除的元素是否为null
if (o == null) {
//若是null遍历删除
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
//若不是遍历删除
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
我们发现在删除的最后,是使用unlink()来进行删除的
E unlink(Node<E> x) {
//这个Node的所有属性拿出来
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//下面的操作就是更新当前节点prev和next,然后把当前节点上的元素设置为null
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
在LinkedList中remove()的方法有很多,原理基本一致,这里不做过多的讲解
-
remove():删除第一个节点 -
remove(int):删除指定位置的节点 -
remove(Object):删除指定元素的节点 -
removeFirst():删除第一个节点 -
removeLast():删除最后一个节点