总览
定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- LinkedList<E>:说明它支持泛型。
-
extends AbstractSequentialList<E>
- AbstractSequentialList 继承自AbstractList,但AbstractSequentialList 只支持按次序访问,而不像 AbstractList 那样支持随机访问。这是LinkedList随机访问效率低的原因之一。
-
implements
- List<E>:说明它支持集合的一般操作。
- Deque<E>:Deque,Double ended queue,双端队列。LinkedList可用作队列或双端队列就是因为实现了它。
- Cloneable:表明其可以调用clone()方法来返回实例的field-for-field拷贝。
- java.io.Serializable:表明该类是可以序列化的。
与ArrayList对比
LinkedList并没有实现RandomAccess,而实现RandomAccess表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。这是LinkedList随机访问效率低的原因之一。
LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:
List list=Collections.synchronizedList(new LinkedList(...));
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节点个数
*/
transient int size = 0;
/**
* 指向头节点的指针
*/
transient Node<E> first;
/**
* 指向尾节点的指针
*/
transient Node<E> last;
构造方法
LinkedList()
- LinkedList(Collection<? extends E> c)
/**
* 构造一个空链表.
*/
public LinkedList() {
}
/**
* 根据指定集合c构造linkedList。先构造一个空linkedlist,在把指定集合c中的所有元素都添加到linkedList中。
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
操作链表的底层方法
linkFirst(E e)
/**
* 在表头添加指定元素e
*/
private void linkFirst(E e) {
final Node<E> f = first;
//新建节点,节点的前指针指向null,后指针原来的头节点
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
//如果原来的头结点为null,更新尾指针,否则使原来的头结点f的前置指针指向新的头结点newNode
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
linkLast(E e)
/**
* 在表尾插入指定元素e
*/
void linkLast(E e) {
final Node<E> l = last;
//新建节点newNode,节点的前指针指向l,后指针为null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
//如果原来的尾结点为null,更新头指针,否则使原来的尾结点l的后置指针指向新的头结点newNode
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
linkBefore(E e, Node<E> succ)
/**
* 在指定节点succ之前插入指定元素e。指定节点succ不能为null。
*/
void linkBefore(E e, Node<E> succ) {
//获得指定节点的前驱
final Node<E> pred = succ.prev;
//新建节点newNode,前置指针指向pred,后置指针指向succ
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
//如果指定节点的前驱为null,将newTouch设为头节点。否则更新pred的后置节点
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
unlink(Node<E> x)
/**
* 删除指定节点,返回指定元素的值
*/
E unlink(Node<E> x) {
// assert x != null;
// 保存指定节点的值
final E element = x.item;
//得到后继节点
final Node<E> next = x.next;
//得到前驱节点
final Node<E> prev = x.prev;
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;
}
unlinkFirst(Node<E> f)
/**
* 删除头结点f,并返回头结点的值.
*/
private E unlinkFirst(Node<E> f) {
//保存头结点的值
final E element = f.item;
// 保存头结点指向的下个节点
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
//如果next为null,将尾节点置为null,否则将next的后置指针指向null
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
//返回被删除的头结点的值
return element;
}
unlinkLast(Node<E> l)
/**
* 删除尾节点并返回尾节点的值
*/
private E unlinkLast(Node<E> l) {
// 保存尾节点的值
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
//如果新的尾节点为null,头结点置为null,否则将新的尾节点的后置指针指向null
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
//返回被删除的尾节点的值
return element;
}
添加
步骤:
- 先用一个变量l指向尾结点,
- 创建新结点
- 尾结点指向新的结点
- 判断原来的尾结点(变量l指向的结点)是否为空,
- 如果为空说明是个空链表,将头结点指向新的结点;
- 原来的尾结点不为空,将原来尾结点(l指向的结点)的prev指向新的结点
add(E e)
/**
* 将元素添加到链表尾部
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* 在表尾插入指定元素e
*/
void linkLast(E e) {
final Node<E> l = last;
//新建节点newNode,节点的前指针指向l,后指针为null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
//如果原来的尾结点为null,更新头指针,否则使原来的尾结点l的后置指针指向新的头结点newNode
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
add(int index, E element)
/**
* 在指定位置添加元素
*/
public void add(int index, E element) {
//检查索引是否处于[0-size]之间
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
addAll(Collection<? extends E> c)
步骤:
- 检查index范围是否在size之内
- toArray()方法把集合的数据存到对象数组中
- 得到插入位置的前驱和后继节点
- 遍历数据,将数据插入到指定位置
/**
* 插入指定集合到链尾
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* 插入指定集合到链尾的指定位置
*/
public boolean addAll(int index, Collection<? extends E> c) {
//1:检查index范围是否在size之内
checkPositionIndex(index);
//2:toArray()方法把集合的数据存到对象数组中
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
//3:得到插入位置的前驱节点和后继节点
Node<E> pred, succ;
//如果插入位置为尾部,前驱节点为last,后继节点为null
if (index == size) {
succ = null;
pred = last;
} else {
//否则,调用node()方法得到后继节点,再得到前驱节点
succ = node(index);
pred = succ.prev;
}
// 4:遍历数据将数据插入
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;
}
//如果插入位置在尾部,重置last节点
if (succ == null) {
last = pred;
}//否则,将插入的链表与先前链表连接起来
else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
addFirst(E e)
/**
* 在表头插入指定元素.
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 在表头添加指定元素e
*/
private void linkFirst(E e) {
final Node<E> f = first;
//新建节点,节点的前指针指向null,后指针原来的头节点
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
//如果原来的头结点为null,更新尾指针,否则使原来的头结点f的前置指针指向新的头结点newNode
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
get方法
get(int index)
/**
* 返回指定索引处的元素
*/
public E get(int index) {
//检查index范围是否在size之内
checkElementIndex(index);
//调用node(index)去找到index对应的node然后返回它的值
return node(index).item;
}
/**
* 返回在指定索引处的非空元素
*/
Node<E> node(int index) {
// 下标小于长度的一半,从头遍历,否则从尾遍历
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
获取头节点(index=0)数据方法:
/**
* 返回链表中的头结点的值.
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 获取表头节点的值,头节点为空抛出异常
*/
public E element() {
return getFirst();
}
/**
* 返回头节点的元素,如果链表为空则返回null
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* 返回队列的头元素,如果头节点为空则返回空
*/
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
区别: getFirst(),element(),peek(),peekFirst() 这四个获取头结点方法的区别在于对链表为空时的处理,是抛出异常还是返回null。
getFirst() 和element() 方法将会在链表为空时,抛出异常
element()方法的内部就是使用getFirst()实现的。它们会在链表为空时,抛出NoSuchElementException
获取尾节点(index=-1)数据方法:
/**
* 返回链表中的尾结点的值.
*/
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
/**
* 返回队列的尾元素,如果尾节点为空则返回空
*/
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
区别: getLast() 方法在链表为空时,会抛出NoSuchElementException,而peekLast() 则不会,只是会返回 null。
根据对象得到索引的方法
indexOf(Object o)
/**
* 正向遍历链表,返回指定元素第一次出现时的索引。如果元素没有出现,返回-1.
*/
public int indexOf(Object o) {
int index = 0;
if (o == null) {
//从头遍历
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
//从头遍历
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
lastIndexOf(Object o)
/**
* 逆向遍历链表,返回指定元素第一次出现时的索引。如果元素没有出现,返回-1.
*/
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
//从尾遍历
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
//从尾遍历
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
删除方法
remove() ,removeFirst(),pop(): 删除头节点
/**
* 删除并返回栈头元素
*/
public E pop() {
return removeFirst();
}
/**
* 删除并返回头节点,如果链表为空,抛出异常
*/
public E remove() {
return removeFirst();
}
/**
* 删除并返回表头元素.
*/
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
removeLast(),pollLast(): 删除尾节点
/**
* 删除并返回表尾元素
*/
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 删除并返回队列的最后个元素,如果尾节点为空,则返回null.
*/
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
区别: removeLast()在链表为空时将抛出NoSuchElementException,而pollLast()方法返回null。
remove(Object o)
/**
* 正向遍历链表,删除出现的第一个值为指定对象的节点
*/
public boolean remove(Object o) {
//LinkedList允许存放Null
//如果删除对象为null
if (o == 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) {
// assert x != null;
// 保存指定节点的值
final E element = x.item;
//得到后继节点
final Node<E> next = x.next;
//得到前驱节点
final Node<E> prev = x.prev;
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;
}
其他方法
contains(Object o)
/**
* 判断链表是否包含指定对象o
*/
public boolean contains(Object o) {
return indexOf(o) != -1;
}
set(int index, E element)
/**
* 替换指定索引处的元素为指定元素element
*/
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
总结
- LinkedList底层是双向链表。
- 有序。
- 元素可重复。链表元素可重复。
- 随机访问效率低,增删效率高。
参考资料:
https://segmentfault.com/a/11...
https://blog.csdn.net/panweiw...
https://github.com/Snailclimb...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。