目录
LinkedList 数据存储的基础结构
单 Node 信息图示
LinkedList 数据存储关系图示
LinkedList 作为栈使用
push(E):入栈操作,在头部添加元素
pop():出栈操作,取出头部元素并将其从栈中移除
peek():查看头部元素,不影响栈的结构
各方法操作效果图示
LinkedList 作为队列使用
add(E):添加元素到队列尾部
peek():查看队列头部元素,不改变队列结构
poll():返回头部元素,并从队列删除该元素
LinkedList 作为双端队列使用
LinkedList 队列和栈对各节点元素操作方法
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
public void push(E e) {
// 添加元素到头部
addFirst(e);
}
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
final Node f = first;
// 根据添加数据初始化新 Node
final Node newNode = new Node<>(null, e, f);
// 将新 Node 设置为头结点
first = newNode;
// 原头结点为 null 的情况
if (f == null)
last = newNode;
else
// 原头结点的 prev 设置为新 Node
f.prev = newNode;
size++;
modCount++;
}
public E pop() {
// 返回并删除头元素
return removeFirst();
}
public E removeFirst() {
final Node f = first;
if (f == null)
// 头部元素为 NULL 则抛异常
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node f) {
// assert f == first && f != null;
// 此处不存在 f 为 null 的情况,
final E element = f.item;
// 当前头结点的下一个元素
final Node next = f.next;
f.item = null;
f.next = null; // help GC
// 变更头结点元素
first = next;
//
if (next == null)
// 当前头结点为 null ,则设置尾结点也为 null
last = null;
else
// 头结点 prev 设置为 null
next.prev = null;
size--;
modCount++;
// 返回头部元素
return element;
}
// 由于不影响栈结构,直接返回头部元素即可
public E peek() {
final Node f = first;
return (f == null) ? null : f.item;
}
offer(E) 方法内部调用了 add(E) 方法,两个方法效果相同
public boolean offer(E e) {
return add(e);
}
public boolean add(E e) {
// 将当前元素添加到队尾
linkLast(e);
return true;
}
void linkLast(E e) {
// 获取队尾元素
final Node l = last;
// 初始化新的元素数据
final Node newNode = new Node<>(l, e, null);
// 将队尾元素设置为当前添加的元素
last = newNode;
if (l == null)
// 队列为空的情况下,新增元素时:头部元素 = 尾部元素 = 当前添加元素
first = newNode;
else
// 原队尾元素 next 为当前添加元素
l.next = newNode;
size++;
modCount++;
}
同方法 element() 的区别为:当头部元素为 null 时,element() 方法会抛 NoSuchElementException 异常,peek() 方法返回 null
public E peek() {
final Node f = first;
return (f == null) ? null : f.item;
}
public E element() {
return getFirst();
}
public E getFirst() {
final Node f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
同方法 remove() 的区别为:当头部元素为 null 时,remove() 方法会抛 NoSuchElementException 异常,poll() 方法返回 null
public E poll() {
final Node f = first;
return (f == null) ? null : unlinkFirst(f);
}
private E unlinkFirst(Node f) {
// assert f == first && f != null;
// 此方法入参 f 一定不为 null
final E element = f.item;
// 队列第二个元素
final Node next = f.next;
f.item = null;
f.next = null; // help GC
// 将头结点设置为原队列第二个元素
first = next;
if (next == null)
// 队列只有 1 个元素的情况下,取出后需改变队尾元素也为 null
last = null;
else
next.prev = null;
size--;
modCount++;
// 返回原队列头部元素
return element;
}
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
双端队列底层实现与栈和队列类似。针对栈和队列的各类操作,双端队列都有对应的方法:
poll() --> pollFirst() pollLast()
offer(E) --> offerFirst(E) offerLast(E)
peek() --> peekFirst() peekLast()
等等等等等
队列和栈的添加元素区别:队列在添加元素到尾部,栈添加元素到头部
两种结构获取元素方式相同,均为从头部取元素
操作 | 新增 | 删除并返回 | 查看 |
---|---|---|---|
头部元素 | push(E) addFirst(E) |
pop() 为 null 时抛异常 poll() remove() 为 null 时抛异常 |
peek() element() 为 null 时抛异常 |
尾部元素 | add(E) offer(E) |