Java 集合:Deque(双端队列)

java.util.Deque 接口代表双端队列(全称为 Double Ended Queue,缩写为 Deque,发音同 "deck",与 "a deck of cards" 中的 "deck" 一致)。它的核心特性是允许在队列的两端执行元素的添加与移除操作,而非仅局限于传统队列的单端入队、单端出队模式。

正因为支持两端操作,Deque 具备极强的灵活性:既可以像普通队列一样遵循 FIFO 规则使用,也能当作栈(Stack)按照 LIFO 规则操作。

从接口继承关系来看,Deque 直接继承了 Queue 接口,因此天然支持所有队列操作方法(如 add()、poll() 等)。虽然它并未继承 Stack 接口,但专门定义了一套栈操作方法(如 push() 入栈、peek() 查看栈顶、pop() 出栈等),完全能实现传统栈的所有功能。

image.png

  

Deque 实现

由于 Deque 本质是一个接口(仅定义规范,不包含具体实现逻辑),因此若要实际使用它,必须实例化其对应的具体实现类。在 Java 集合 API 中,常用的 Deque 实现类主要有以下几种,可根据实际需求选择:

  • java.util.LinkedList  是 Deque 接口的经典实现类,同时也实现了 List 接口。它基于双向链表数据结构实现,因此对双端队列的两端操作(如 addFirst()、addLast()、pollFirst()、pollLast() 等)均能高效完成,时间复杂度为 O (1)。

  • java.util.ArrayDeque  是基于动态数组实现的 Deque 实现类,专为双端队列操作优化。它通过循环数组结构(数组尾部与头部逻辑相连)支持高效的两端操作,addFirst()、addLast()、pollFirst()、pollLast() 等核心方法的时间复杂度均为 O (1),且随机访问效率高于 LinkedList。

  

Deque 创建

在使用双端队列(Deque)前,必须先创建其具体实现类的实例—— 因为 Deque 本身是一个仅定义操作规范的接口,不包含实际的功能实现逻辑,无法直接实例化。例如:

// 不带泛型版本
// 基于双向链表的 Deque 实例
Deque deque1 = new LinkedList();
// 基于动态数组的 Deque 实例
Deque deque2 = new ArrayDeque();

// 带泛型
// 基于双向链表的 Deque 实例
Deque<String> deque1 = new LinkedList<>();
// 基于动态数组的 Deque 实例
Deque<String> deque2 = new ArrayDeque<>();

  

Deque 泛型

默认情况下,你可以将任何对象放入 Deque 中,例如:

Deque deque = new LinkedList();
deque.add("hello world");
deque.add(true);
deque.add(3.14f);

不过,不推荐你这样做,你可通过使用 Java 泛型,限制能够插入到 Deque 中的对象类型,例如:

Deque<String> deque = new LinkedList<String>();

上面代码,现在 Deque 只能插入 String 的实例。之后,你可以访问并迭代其元素,而无需对它们进行强制类型转换。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removeValue = deque.remove();
for(String val : deque) {
    System.out.println(val);
}

  

Deque 添加元素

正如本 Deque 开头所提到的,你可以在 Deque 开头和末尾都添加元素。 Deque 接口包含以下用于向其中添加元素的方法:

add()

可以使用 add() 方法在 Deque 的开头和末尾添加元素,例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

如果无法将元素插入 Deque,add() 方法会抛出异常。这与 offer() 方法不同,若无法插入元素,offer() 方法会返回 false。实际上,add() 方法是从 Queue 接口继承而来的。

addLast()

addLast() 方法也会向 Deque 的末尾(尾部)添加一个元素。这是 Deque 接口中从 Queue 接口继承的add() 方法功能相当的方法。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.addLast("four");
System.out.println(deque); //[one, two, three, four]

注意,如果无法将元素插入 Deque,addLast() 方法会抛出异常。这与 offerLast() 方法不同,若无法向 Deque 添加元素,offerLast() 方法会返回 false。

addFirst()

要在 Deque 的开头(头部)而非末尾添加元素,你需要调用 addFirst() 方法,例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.addFirst("four");
System.out.println(deque); //[four, one, two, three]

注意,如果无法将元素添加到 Deque 的开头,addFirst() 方法会抛出异常。这与 offerFirst() 方法不同,若无法在 Deque 的开头插入元素,offerFirst() 方法会返回 false。

offer()

offer() 方法会将元素添加到 Deque 的末尾(尾部)。如果元素添加成功,offer() 方法会返回 true。如果元素添加失败(例如,双端队列已满),offer() 方法会返回false。这与 add() 方法不同,当向 Deque 末尾添加元素失败时,add() 方法会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.offer("four");
System.out.println(deque); //[one, two, three, four]

offerLast()

offerLast() 方法会像 offer() 方法一样,将元素添加到 Deque 的末尾(尾部)。方法名 offerLast() 更明确地说明了元素被添加到 Deque 的哪个位置。如果元素添加成功,offerLast() 方法会返回 true。如果元素添加失败(例如,双端队列已满),offerLast() 方法会返回 false。这与 addLast() 方法不同,当向 Deque 末尾添加元素失败时,addLast() 方法会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.offerLast("four");
System.out.println(deque); //[one, two, three, four]

offerFirst()

offerFirst() 方法会将元素添加到 Deque 的头部。如果元素添加成功,offerFirst() 方法会返回 true。如果元素添加失败(例如,双端队列已满),offerFirst() 方法会返回 false。这与 addFirst() 方法不同,当向 Deque 开头添加元素失败时,addFirst() 方法会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.offerFirst("four");
System.out.println(deque); //[four, one, two, three]

push()

push() 方法会向  Deque 的头部添加一个元素。如果添加元素失败(例如,双端队列已满),push() 方法会抛出异常。这与 addFirst() 方法的工作方式类似。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.push("four");
System.out.println(deque); //[four, one, two, three]

  

Deque 查看元素

你可以查看 Deque 的第一个和最后一个元素。查看元素指的是获取该元素的引用,而不将其从 Deque 中移除。你可以使用以下方法查看 Deque 的第一个和最后一个元素:

peek()

peek() 方法会从 Deque 的开头部返回第一个元素,且不会将其移除。如果双端队列为空,peek() 方法会返回 null。例如:

Deque<String> deque = new LinkedList<>();
String firstElement = deque.peek();
System.out.println("firstElement=" + firstElement); //firstElement=null

deque.add("one");
deque.add("two");
deque.add("three");

firstElement = deque.peek();
System.out.println("firstElement=" + firstElement); //firstElement=one

peekFirst()

peekFirst() 方法会从 Deque 的头部返回第一个元素,且不会将其移除。如果双端队列为空,peekFirst() 方法会返回 null。这与 peek() 方法的工作方式类似,但 peekFirst() 这个方法名更能说明是从 Deque 的哪一端进行查看。例如:

Deque<String> deque = new LinkedList<>();
String firstElement = deque.peekFirst();
System.out.println("firstElement=" + firstElement); //firstElement=null

deque.add("one");
deque.add("two");
deque.add("three");

firstElement = deque.peekFirst();
System.out.println("firstElement=" + firstElement); //firstElement=one

peekLast()

要查看 Deque 的最后一个元素,可以使用 peekLast() 方法。如果双端队列为空,peekLast() 将返回null。例如:

Deque<String> deque = new LinkedList<>();
String last = deque.peekLast();
System.out.println("last=" + last); //last=null

deque.add("one");
deque.add("two");
deque.add("three");

last = deque.peekLast();
System.out.println("last=" + last); //last=three

getFirst()

getFirst() 方法从 Deque 的头部返回第一个元素,且不会将其移除。如果双端队列为空,getFirst() 会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
String first;
try {
    first = deque.getFirst();
    System.out.println("first=" + first);
} catch (NoSuchElementException e) {
    System.out.println("NoSuchElementException error"); //NoSuchElementException error
}

deque.add("one");
deque.add("two");
deque.add("three");

first = deque.getFirst();
System.out.println("first=" + first); //first=one

getLast()

要查看 Deque 的最后一个元素,可以使用 getLast() 方法。如果双端队列为空,getLast() 会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
String last;
try {
    last = deque.getLast();
    System.out.println("last=" + last);
} catch (NoSuchElementException e) {
    System.out.println("NoSuchElementException error"); //NoSuchElementException error
}

deque.add("one");
deque.add("two");
deque.add("three");

last = deque.getLast();
System.out.println("last=" + last); //first=one

  

Deque 移除元素

要从 Deque 中移除元素,可以使用以下方法之一:

remove()

remove() 方法会移除 Deque 的第一个元素,也就是 Deque 头部的元素。实际上,remove() 方法是从Queue 接口继承而来的。该方法会返回从 Deque 中移除的元素。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.remove();
System.out.println(removedElement); //one
System.out.println(deque); //[two, three]

注意:如果双端队列(Deque)为空,remove() 会抛出异常。

removeFirst()

removeFirst() 方法也会从 Deque 中移除第一个元素,即 Deque 头部的元素。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.removeFirst();
System.out.println(removedElement); //one
System.out.println(deque); //[two, three]

注意:如果双端队列(Deque)为空,removeFirst() 会抛出异常。这与 pollFirst() 不同,若双端队列为空,pollFirst() 会返回null。

removeLast()

removeLast() 方法会移除 Deque 的最后一个元素,即 Deque 尾部的元素。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.removeLast();
System.out.println(removedElement); //three
System.out.println(deque); //[one, two]

注意:如果双端队列为空,removeLast() 将抛出异常。

poll()

poll() 方法会从 Deque 的开头移除一个元素。如果 Deque 为空,poll() 会返回 null。这与 remove() 不同,若双端队列为空,remove() 会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.poll();
System.out.println(removedElement); //one
System.out.println(deque); //[two, three]

pollFirst()

pollFirst() 方法会从 Deque 的开头移除一个元素,就像 poll() 方法一样。pollFirst() 这个方法名更能说明该方法是从哪里移除元素的。如果 Deque 为空,pollFirst() 会返回 null。这与 removeFirst() 不同,若双端队列为空,removeFirst() 会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.pollFirst();
System.out.println(removedElement); //one
System.out.println(deque); //[two, three]

pollLast()

pollLast() 方法会从 Deque 的尾部移除一个元素。如果 Deque 为空,pollLast() 会返回 null。这与 removeLast() 不同,若 Deque 为空,removeLast() 会抛出异常。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.pollLast();
System.out.println(removedElement); //three
System.out.println(deque); //[one, two]

pop()

pop() 方法会从 Deque 的头部移除一个元素。如果移除元素失败,例如 Deque 为空时,pop() 方法会抛出异常。这与 removeFirst() 方法的工作方式类似。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

String removedElement = deque.pop();
System.out.println(removedElement); //one
System.out.println(deque); //[two, three]

  

Deque 检查否包含元素

你可以使用 Deque 的 contains() 方法来检查 Deque 是否包含某个给定的元素。如果 Deque 包含该元素,contains() 方法会返回 true;如果不包含,则返回 false。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

System.out.println("包含“two”元素?" + deque.contains("two")); //包含“two”元素?true
System.out.println("包含“four”元素?" + deque.contains("four")); //包含“four”元素?false

  

Deque 大小

Deque 的 size() 方法会返回调用该方法时 Deque 中存储的元素数量。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

System.out.println("队列大小 " + deque.size()); //队列大小 3

  

Deque 遍历元素

你也可以迭代 Deque 的所有元素,而不是一次只处理一个元素。你可以通过三种方式迭代 Deque 的元素:

通过迭代器迭代元素

迭代 Deque 元素的第一种方法是从 Deque 中获取一个迭代器(Iterator),并通过该迭代器来迭代元素。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

Iterator<String> iterator = deque.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}

通过增强型for循环迭代元素

迭代 Deque 元素的第二种方法是使用 Java 中的增强型 for 循环。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

for(String item : deque) {
    System.out.println(item);
}

通过 Stream API 迭代元素

迭代 Deque 元素的第三种方法是使用 Java 中的 Stream API 进行迭代。例如:

Deque<String> deque = new LinkedList<>();
deque.add("one");
deque.add("two");
deque.add("three");

deque.stream().forEach(item -> {
    System.out.println(item);
});

更多信息请查看 https://docs.oracle.com/javase/8/docs/api/java/util/Deque.html API 文档。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号