主要内容:
- LinkedList继承关系、关键属性、构造函数
- 数据结构
- 插入、删除、修改以及查找元素
- 与ArrayList比较
LinkedList概述
介绍LinkedList,就会想到ArrayList,两者都实现了List接口。但ArrayList底层是基于数组实现,随机访问优于LinkedList;而LinkedList底层基于链表实现,插入、删除操作效率优于LinkedList。
- 基于链表实现。
- 插入、删除操作快速,但随机访问元素缓慢。
- 非线程安全,创建线程安全的LinkedList可以使用
Collections.synchronizedList
。
ArrayList map = Collections.synchronizedList(new LinkedList());
源码分析
继承关系
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 继承AbstractSequentialList抽象类,序列访问数据,类方法是通过迭代器实现的
- 实现List接口,有序的队列
- 实现Deque接口,可以当作双端队列使用
- 实现java.io.Serialization接口,支持序列化
- 实现Cloneable接口,支持对象克隆,浅复制
关键属性
//实际存储的元素个数
transient int size = 0;
//头节点
transient Node<E> first;
//尾部节点
transient Node<E> last;
节点类
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;
}
}
构造函数
//构造空的LinkedList
public LinkedList() {
}
//构造指定集合的LinkedList,并且按集合迭代的元素顺序排序
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
插入
插入元素
插入单个元素的方法主要有boolean add(E e)
和void add(int index, E element)
。
-
boolean add(E e)
:表示将指定元素插入到LinkedList尾部,插入链表尾部调用的方法是linkLast(E e)
。
public boolean add(E e) {
linkLast(e);//插入尾部
return true;
}
void linkLast(E e) {//插入LinkedList的尾部
final Node<E> l = last;
//新建一个节点,前一个节点是原来的尾节点,下一个节点为null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;//将新建的节点作为尾节点
if (l == null)
first = newNode;//空LinkedList
else
l.next = newNode;
size++;
modCount++;
}
-
void add(int index, E element)
:表示将指定元素插入到index位置处。如果插入的位置在链表尾部,直接插入到链表尾部;如果插入的位置不在链表尾部,要调用的方法是linkBefore(E e, Node<E> succ)
,还需要调用node(int index)
获得某位置上的元素。
public void add(int index, E element) {
checkPositionIndex(index);//判断位置的范围
if (index == size)
linkLast(element);//插入尾部
else
linkBefore(element, node(index));//将元素插入到index位置节点之前
}
void linkBefore(E e, Node<E> succ) {//将元素插入到succ节点前
// assert succ != null;
final Node<E> pred = succ.prev;//获取succ前一个节点
final Node<E> newNode = new Node<>(pred, e, succ);//创建新节点,插入到pred和succ之间
succ.prev = newNode;//改变succ的前一个节点的引用
if (pred == null)
first = newNode;//LinkedList只有一个元素
else
pred.next = newNode;
size++;
modCount++;
}
Node<E> node(int index) {//获取index位置上的节点
// assert isElementIndex(index);
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;
}
}
插入集合元素
插入集合的方法主要有boolean addAll(Collection<? extends E> c)
和boolean addAll(int index, Collection<? extends E> c)
。
//按照集合迭代器的顺序,将集合的元素插入到链表尾部
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//从指定位置上开始,插入集合的元素
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//防止越界
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;//pred用来表示插入节点的前一个节点,succ表示后一个节点
if (index == size) {//元素是插入到链表尾部
succ = null;
pred = last;
} else {//元素插入到链表中
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
删除
删除元素方法主要有E remove(int index)
、boolean remove(Object o)
和实现Deque接口中的E remove()
。
-
E remove(int index)
表示删除索引位置上的元素,boolean remove(Object o)
表示删除首次出现的指定元素,都会调用unlink(Node<E> x)
。
//删除索引位置上的元素
public E remove(int index) {
checkElementIndex(index);//防止越界
return unlink(node(index));
}
//移除首次出现的指定元素
public boolean remove(Object o) {
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;
}
E unlink(Node<E> x) {//删除节点x
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;//要删除节点的后一个节点
final Node<E> prev = x.prev;//要删除节点的前一个节点
if (prev == null) {//x为头节点
first = next;
} else {
prev.next = next;
x.prev = null;//释放x的前节点的引用
}
if (next == null) {//x为尾节点
last = prev;
} else {
next.prev = prev;
x.next = null;//释放x的后节点的引用
}
x.item = null;
size--;
modCount++;
return element;
}
-
E remove()
:删除头节点。
//删除头节点
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)//头节点为null,抛出NoSuchElementException
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {//删除头节点,并返回头节点的值
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;//头节点的下一个节点
f.item = null;//释放头节点的值
f.next = null; //释放头节点的后一个节点引用
first = next;
if (next == null)//删除头节点后LinkedList为空
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
修改
将数组中指定位置上的元素替换掉,返回以前位于该位置上的元素。
public E set(int index, E element) {//替代index位置上的节点值
checkElementIndex(index);//防止越界
Node<E> x = node(index);//获取index位置处的节点
E oldVal = x.item;
x.item = element;//替代
return oldVal;
}
查找
返回指定位置上的元素。
public E get(int index) {//获取index位置处的节点值
checkElementIndex(index);//防止越界
return node(index).item;
}
与ArrayList比较
LinkedList比ArrayList插入、删除更快,但这个说法不是很准确。
- ArrayList插入、删除操作时,查找到该位置的元素迅速,但是复制元素消耗时间
- LinkedList插入、删除操作时,查找需要操作的Entry元素需要消耗一定时间,但改变该节点前后引用地址快速