Deque是一个双端队列接口,继承自Queue接口,Deque的实现类是LinkedList、ArrayDeque、LinkedBlockingDeque,其中LinkedList是最常用的。
Deque是一个线性collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。
此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。
双端队列的接口:
public interface DequeInterface<T> {
public void addToFront(T newEntry);
public void addToBack(T newEntry);
public T removeFront();
public T removeBack();
public T getFront();
public T getBack();
public boolean isEmpty();
public void clear();
public int size();
}
实现接口的类
使用LinkedList来实现:
import java.util.LinkedList;
/**
* 双向链表/LinkedList实现双端队列。
*/
public class LinkedDeque<T> implements DequeInterface<T> {
private LinkedList<T> list;
public LinkedDeque() {
// 初始化链表
list = new LinkedList<T>();
}
@Override
public void addToFront(T newEntry) {
// 从队列的头部近入
list.addFirst(newEntry);
}
@Override
public void addToBack(T newEntry) {
// 从队列的尾部近入
list.addLast(newEntry);
}
@Override
public T removeFront() {
// 移除首位元素
return list.removeFirst();
}
@Override
public T removeBack() {
// 移除尾部元素
return list.removeLast();
}
@Override
public T getFront() {
// 类似于peek(),将首位元素返回,不进行操作。
return list.getFirst();
}
@Override
public T getBack() {
// 得到尾部元素,不进行操作
return list.getLast();
}
@Override
public boolean isEmpty() {
// 容量,可以选择size==0,也可以直接调用siEmpty
return list.isEmpty();
}
@Override
public void clear() {
// 清空即可
list.clear();
}
@Override
public int size() {
return list.size();
}
}
自定义一个双向链表,替换掉LinkedList:
package com.openlab.queue;
import java.util.LinkedList;
/**
*双向链表实现双端队列。
*/
public class LinkedDeque<T> implements DequeInterface<T> {
// private LinkedList<T> list;
// 使用一个值保存头结点,一个保存尾结点。 没有必要使用循环链表,因为判断起来比较麻烦。
private Node first;
private Node last;
int size;
public LinkedDeque() {
// 初始化链表
// list = new LinkedList<T>();
// 初始化属性
first = null;
last = null;
size = 0; // 虽然有默认初始化,此处仍需初始化,养成一个好的编码习惯
}
class Node {
T data;
Node prev;
Node next;
public Node(T data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
public Node(T data) {
this(data, null, null);
}
}
@Override
public void addToFront(T newEntry) {
// 从队列的头部近入
Node newNode = new Node(newEntry);
if (isEmpty()) {
// 尾结点也是头结点 <---> 头结点也是尾结点
last = newNode;
} else {
// 新节点与首节点建立双向链接
first.prev = newNode;
newNode.next = first;
}
first = newNode;
size++;
// list.addFirst(newEntry);
}
@Override
public void addToBack(T newEntry) {
// 从队列的尾部近入
Node newNode = new Node(newEntry);
if (isEmpty()) {
// 尾结点也是头结点 <---> 头结点也是尾结点
first = newNode;
} else {
// 新节点与尾节点建立双向链接
newNode.prev = last;
last.next = newNode;
}
last = newNode;
size++;
// 从队列的尾部近入
// list.addLast(newEntry);
}
@Override
public T removeFront() {
// 移除首位元素
// return list.removeFirst();
if (isEmpty()) {
// 抛出异常
}
Node front = first;
// 首节点后移
first = first.next;
if (first == null) {
last = null;
} else {
// 断开双向链接
first.prev = null;
front.next = null;
}
size--;
return front.data;
}
@Override
public T removeBack() {
if (isEmpty()) {
// 抛出异常
}
Node back = last;
last = last.prev;
if (last == null) {
first = null;
} else {
// 断开双向链接
last.next = null;
back.prev = null;
}
size--;
return back.data;
// 移除尾部元素
// return list.removeLast();
}
@Override
public T getFront() {
// 类似于peek(),将首位元素返回,不进行操作。
// return list.getFirst();
if (isEmpty()) {
// 抛出异常
}
return first.data;
}
@Override
public T getBack() {
// 得到尾部元素,不进行操作
// return list.getLast();
if (isEmpty()) {
// 抛出异常
}
return last.data;
}
@Override
public boolean isEmpty() {
// 容量,可以选择size==0,也可以直接调用siEmpty
// return list.isEmpty();
// 当first 和 back 都为空时,双向链表为空
return (first == null) && (last == null);
}
@Override
public void clear() {
// 双向链表实现,必须考虑两个对象相互引用的情况,如果出现,那么GC就不会清理这个内存,从而导致内存泄漏。
// 所以我们需要遍历链表并将其置为null
for (Node x = first; x != null && x != last;) {
Node next = x.next;
x.data = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
// 清空即可
// list.clear();
}
@Override
public int size() {
return size;
// return list.size();
}
}