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() 出栈等),完全能实现传统栈的所有功能。
由于 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 deque1 = new LinkedList(); // 基于动态数组的 Deque 实例 Deque deque2 = new ArrayDeque(); // 带泛型 // 基于双向链表的 Deque 实例 Deque<String> deque1 = new LinkedList<>(); // 基于动态数组的 Deque 实例 Deque<String> deque2 = new ArrayDeque<>();
默认情况下,你可以将任何对象放入 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 接口包含以下用于向其中添加元素的方法:
可以使用 add() 方法在 Deque 的开头和末尾添加元素,例如:
Deque<String> deque = new LinkedList<>(); deque.add("one"); deque.add("two"); deque.add("three");
如果无法将元素插入 Deque,add() 方法会抛出异常。这与 offer() 方法不同,若无法插入元素,offer() 方法会返回 false。实际上,add() 方法是从 Queue 接口继承而来的。
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。
要在 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() 方法会将元素添加到 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() 方法会像 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() 方法会将元素添加到 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() 方法会向 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 的第一个和最后一个元素:
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() 方法会从 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
要查看 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() 方法从 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
要查看 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 中移除元素,可以使用以下方法之一:
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() 方法也会从 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() 方法会移除 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() 方法会从 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() 方法会从 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() 方法会从 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() 方法会从 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 的 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 的 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 中获取一个迭代器(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()); }
迭代 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); }
迭代 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 文档。