Java双端队列
Java Deque接口java.util.Deque表示一个双头队列,表示一个队列,我们可以在该队列的两端添加和删除元素。名称Deque是Double Ended Queue的缩写。单词Deque的发音像纸牌的" deck"一样,是" deck"。
因为我们可以从Java Deque的两端入队和出队,所以可以将Deque用作队列和堆栈。 JavaDeque
接口扩展了Java Queue接口。这意味着在使用Deque时可以使用所有Java Queue方法。 Deque接口没有扩展Java Stack接口,但是Deque接口定义了使我们能够执行通常在堆栈上执行的相同操作(push,peek,pop)的方法。
双端队列实现
由于JavaDeque
是一个接口,因此我们需要实例化该接口的具体实现才能使用它。我们可以在Java Collections API中的以下Deque
实现中进行选择:
- java.util.LinkedList
- java.util.ArrayDeque
LinkedList类是一个非常标准的Deque和Queue实现。它在内部使用链接列表来对队列或者双端队列进行建模。
JavaArrayDeque
类将其元素内部存储在数组中。如果元素数超过数组中的空间,则会分配一个新的数组,并且所有元素都将移到该数组中。换句话说,即使将其元素存储在数组中," ArrayDeque"也会根据需要增长。
创建双端队列
在使用Java Deque之前,必须创建实现Deque接口的类之一的实例。这是通过创建LinkedList
实例创建JavaDeque
实例的示例:
Deque deque = new LinkedList();
这是通过创建ArrayDeque
实例来创建JavaDeque
的另一个示例:
Deque deque = new ArrayDeque();
通用双端队列
默认情况下,我们可以将任何Object
放入JavaDeque
中。但是,使用Java泛型可以限制我们可以插入到Deque中的对象的类型。这是一个例子:
Deque<MyObject> deque = new LinkedList<MyObject>();
现在,这个"双端队列"只能插入" MyObject"实例。然后,我们可以访问和迭代其元素,而无需强制转换它们。外观如下:
MyObject myObject = deque.remove(); for(MyObject anObject : deque){ //do someting to anObject... }
在声明Java Deque变量时始终指定通用类型被认为是一种好习惯。这样,编译器可以检查将什么类型插入到双端队列中,并且当我们再次将它们从双端队列中取出时,不必强制转换对象。另外,下一个阅读代码的人会更清楚此Deque应该包含哪种对象。
有关Java泛型的更多信息,请参见Java泛型教程。
将元素添加到双端队列
如本JavaDeque
教程的开头所述,我们可以将元素添加到Deque
的开头和结尾。 JavaDeque
接口包含以下用于向其添加元素的方法:
- add()
- addLast()
- addFirst()
- offer()
- offerFirst()
- offerLast()
这些方法将在以下各节中说明。
add()
使用add()方法将元素添加到Deque的开头。这是在Java Deque的末尾添加元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("element 1");
如果不能将元素插入Deque中,则add()方法将引发异常。这与offer()
方法不同,后者无法插入元素时将返回false。
实际上,add()方法是从Queue接口继承的。
addLast()
addLast()方法还将元素添加到Java Deque的末尾。这是Deque接口,等效于从Queue接口继承的add()方法。这是一个使用addLast()方法向Java Deque实例添加元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.addLast("element 1");
如果不能将元素插入Deque中,则addLast()方法将引发异常。这与offerLast()
方法不同,如果无法将元素添加到双端队列,该方法将返回" false"。
addFirst()
要在Java Deque的开头(头)而不是Java的末尾添加元素,请调用addFirst()方法。这是在Java Deque的开头(头部)添加元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.addFirst("element 1");
如果不能将元素添加到Deque的开头,则addFirst()方法将引发异常。这与offerFirst()
方法不同,后者无法在Deque的开头插入元素时返回false。
offer()
offer()
方法会在Deque的末尾添加一个元素。如果添加元素成功,那么offer()方法将返回true。如果添加元素失败,例如如果双端队列已满,则" offer()"方法返回" false"。这与add()
方法不同,后者会引发异常,因为在Deque末尾添加元素失败。这是一个使用offer()
方法将元素添加到Java Deque末尾的示例:
Deque<String> deque = new ArrayDeque<>(); deque.offer("element 1");
offerLast()
像offer()
一样,offerLast()
方法会在Deque的末尾添加一个元素。方法名称" offerLast()"只不过是关于将元素添加到双端队列的位置而已。如果添加元素成功,则offerLast()方法将返回true。如果添加元素失败,例如如果双端队列已满,则" offerLast()"方法返回" false"。这与addLast()方法不同,后者会引发异常,因为在Deque的末尾添加元素失败。这是一个如何使用offerLast()方法将元素添加到Java Deque末尾的示例:
Deque<String> deque = new ArrayDeque<>(); deque.offerLast("element 1");
offerFirst()
offerFirst()
方法将一个元素添加到双端队列的开头(头)。如果添加元素成功,那么offerFirst()方法将返回true。如果添加元素失败,例如如果双端队列已满,则" offerFirst()"方法返回" false"。这与addFirst()方法不同,后者会引发异常,因为在Deque的开头添加了一个元素而失败。这是一个如何使用offerFirst()
方法将元素添加到Java Deque开头的示例:
Deque<String> deque = new ArrayDeque<>(); deque.offerFirst("element 1");
push()
push()
方法将一个元素添加到Java Deque方法的开头(头)。如果添加元素失败,例如,如果Deque已满,则push()
方法将引发异常。这类似于addFirst()方法的工作方式。这是一个使用push()
方法将元素添加到Java Deque开头的示例:
Deque<String> deque = new LinkedList<>(); deque.push("element 0");
查看Deque中的元素
我们可以查看Java Deque的第一个和最后一个元素。窥视元素意味着无需从Deque中移除元素即可获得对该元素的引用。我们可以使用以下方法查看Java Deque的第一个和最后一个元素:
- peek()
- peekFirst()
- peekLast()
- getFirst()
- getLast()
以下各节将介绍这两种方法。
peek()
peek()
方法从Java Deque的开头(头)返回第一个元素,而不删除它。如果双端队列为空,则peek()返回null。这是一个使用peek()
方法窥视JavaDeque
的第一个元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); deque.add("last element"); String firstElement = deque.peek();
运行此代码后," firstElement"将指向添加到Deque中的第一个String元素:" first element"。
peekFirst()
peekFirst()
方法从Java Deque的开头(头)返回第一个元素,而不删除它。如果双端队列为空,则peekFirst()返回null。这与peek()
的工作方式类似,但是方法名称peekFirst()
稍微说明了我们在Deque的哪一端看。这是一个使用peekFirst()
方法来窥视JavaDeque
的第一个元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); deque.add("last element"); String firstElement = deque.peekFirst();
运行此代码后," firstElement"将指向添加到Deque中的第一个String元素:" first element"。
peekLast()
要查看Java Deque的最后一个元素,可以使用peekLast()
方法。如果双端队列为空,则peekLast()
将返回null
。这是一个使用peekLast()
方法来窥视JavaDeque
的最后一个元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); deque.add("last element"); String lastElement = deque.peekLast();
运行此Java示例之后,变量lastElement
将指向字符串last element
,因为该字符串是添加到Deque
中的最后一个元素。
getFirst()
" getFirst()"方法从Java Deque的开头(头部)返回第一个元素,而不会将其删除。如果双端队列为空,则getFirst()会引发异常。这是使用getFirst()方法窥视Java Deque的第一个元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); deque.add("last element"); String firstElement = deque.getFirst();
运行此代码后," firstElement"将指向添加到Deque中的第一个String元素:" first element"。
getLast()
要查看Java Deque的最后一个元素,可以使用getLast()方法。如果双端队列为空,则getLast()将返回null。这是使用getLast()方法窥视Java Deque的最后一个元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); deque.add("last element"); String lastElement = deque.getLast();
运行此Java示例之后,变量lastElement
将指向字符串last element
,因为该字符串是添加到Deque
中的最后一个元素。
从双端队列中删除元素
要从Java Deque中删除元素,可以使用以下方法之一:
- remove()
- removeFirst()
- removeLast()
- poll()
- pollFirst()
- pollLast()
以下各节将说明每种方法。
remove()
remove()
方法删除JavaDeque
的第一个元素。这是"双端队列"开头的元素。实际上,remove()
方法是从Queue
接口继承的。 remove()
方法返回从'Deque'中删除的元素。这是一个使用remove()
方法删除Java'Deque'第一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); String removedElement = deque.remove();
如果双端队列为空,则remove()
将引发异常。这与poll()
不同,后者在Deque为空时返回null。
removeFirst()
removeFirst()
方法还从'Deque'中删除第一个元素,即Deque头部的元素。这是一个使用removeFirst()
方法删除JavaDeque
的第一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); String removedElement = deque.removeFirst();
如果双端队列为空,则removeFirst()
将引发异常。这与pollFirst()
不同,后者在Deque为空时返回null。
removeLast()
removeLast()
方法将删除'Deque'的最后一个元素,这意味着该元素位于'Deque'尾部。这是一个使用removeLast()
方法删除JavaDeque
的最后一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); deque.add("element 1"); deque.add("element 2"); String removedElement = deque.removeLast();
运行该Java示例后,removedElement
变量将指向String对象element 2
,因为当调用removeLast()
时,该元素是Deque
的最后一个元素。
如果双端队列为空,则removeLast()
将引发异常。这与pollLast()
不同,后者在Deque为空时返回null。
poll()
poll()
方法从Deque的开头删除一个元素。如果双端队列为空,则poll()
返回null
。这与remove()
不同,后者在Deque为空时抛出异常。这是一个使用poll()
方法从Java Deque中删除第一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); deque.add("element 1"); deque.add("element 2"); String removedElement = deque.poll();
pollFirst()
pollFirst()
方法从Deque的开头删除一个元素,就像poll()
一样。方法名称pollFirst()
只是关于该方法从何处删除元素的说法。如果双端队列为空,则pollFirst()
返回null
。这与removeFirst()
不同,后者在Deque为空时抛出异常。这是一个使用pollFirst()
方法从Java Deque中删除第一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); deque.add("element 1"); deque.add("element 2"); String removedElement = deque.pollFirst();
pollLast()
pollLast()
方法从双端队列的末尾删除一个元素。如果双端队列为空,则pollLast()
返回null
。这与removeLast()
不同,后者在Deque为空时抛出异常。这是一个使用pollLast()
方法从Java Deque中删除最后一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); deque.add("element 1"); deque.add("element 2"); String removedElement = deque.pollLast();
pop()
pop()方法从Java Deque的开头(头部)删除元素。如果删除元素失败,例如如果Deque为空,则pop()方法将引发异常。这类似于removeFirst()
方法的工作方式。这是一个使用pop()
方法从Java Deque中删除第一个元素的示例:
Deque<String> deque = new LinkedList<>(); deque.push("element 0"); String removedElement = deque.pop();
检查双端队列是否包含元素
我们可以使用Java Dequecontains()
方法检查Deque是否包含给定元素。如果Deque包含元素,则" contains()"方法将返回" true",否则返回" false"。这是一个使用contains()
方法检查Java Deque是否包含特定元素的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); boolean containsElement1 = deque.contains("first element"); boolean containsElement2 = deque.contains("second element");
运行此代码后,因为Deque包含Java字符串"第一个元素",所以" containsElement1"变量将具有值" true",而由于Deque不包含Java字符串" containsElement2"将包含值" false",第二要素"。
双端队列大小
Java Deque的size()方法返回调用该方法时存储在Java Deque中的元素数。这是一个使用其Deque()方法获取Java Deque中元素数量的示例:
Deque<String> deque = new ArrayDeque<>(); deque.add("first element"); deque.add("second element"); int size = deque.size();
运行此代码后,size
变量将包含值'2',因为Deque在调用size()
时包含2个元素。
重复双端队列元素
我们还可以迭代Java Deque的所有元素,而不仅仅是一次处理一个元素。我们可以通过两种方式迭代Deque的元素:
- 使用
迭代器
。 - 使用for-each循环。
这两个选项将在以下各节中进行说明。注意,在迭代过程中获得元素的顺序取决于具体的Deque实现。但是,不管实现如何,迭代元素的方法都是相同的。
通过迭代器迭代元素
迭代"双端队列"的元素的第一种方法是从"双端队列"中获取一个迭代器,并通过该迭代器来迭代元素。这是一个通过Iterator迭代Java Deque元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); deque.add("element 1"); deque.add("element 2"); Iterator<String> iterator = deque.iterator(); while(iterator.hasNext(){ String element = iterator.next(); }
通过For-Each循环迭代元素
迭代Deque元素的第二种方法是在Java中使用for-each循环。这是一个通过for-each循环迭代JavaDeque
的元素的示例:
Deque<String> deque = new LinkedList<>(); deque.add("element 0"); deque.add("element 1"); deque.add("element 2"); for(String element : deque) { System.out.println(element); }
JavaDoc中的更多详细信息
使用Deque可以做更多的事情,但是我们必须查看JavaDoc以获得更多详细信息。本文重点介绍两个最常见的操作:添加/删除元素和迭代元素。