Java中棧和隊列的使用及區別


1、Stack(棧)

  在java8中,Stack的官方文檔介紹如下:

  public class Stack<E> extends Vector<E>
  The Stack class represents a last-in-first-out (LIFO) stack of objects.It extends class Vector with five operations that allow a vector to be treated as a stack.
 The usual push and pop operations are provided, as well as a method to peek at the top item on the stack, a method to test for whether the stack is empty, and a method to search the stack for an item and discover how far it is from the top.
 When a stack is first created, it contains no items.
  A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:
  Deque<Integer> stack = new ArrayDeque<Integer>();

  大致意思為:Stack類表示對象的后進先出(LIFO:先進后出)。它使用五個操作擴展了Vector類,這些操作允許將矢量視為棧。提供了通常的推入(push)和彈出(pop)操作,以及一種查看棧頂部的方法(peek),一種用於測試堆棧是否為空的方法(empty)以及一種用於在棧中搜索元素並發現其和頂部top距離的方法(search)。從頂部開始。 首次創建堆棧時,它不包含任何項目。

  除此之外Deque接口及其實現提供了一組更完整和一致的LIFO堆棧操作,應優先使用此類。例如:

     Deque<Integer> stack = new ArrayDeque<Integer>();

  從上面的信息我們可以得出Stack有5個常用的方法

Modifier and Type Method and Description
boolean empty() :測試棧是否為空
E peek() :在不將其從棧中移除的情況下,查看該棧頂部的對象。在棧為空的時候會報EmptyStackException
E pop() :刪除此棧頂部的對象,並將該對象作為此函數的值返回。在棧為空的時候會報EmptyStackException
E push(E item) :將一個項目推送到此堆棧的頂部。
int search(Object o) :返回對象在此棧上的從1開始的位置。對象不存在則會返回-1

  我們可以看到Satck的功能基本上已經滿足我們的要求了,可是在文檔上依然推薦我們使用Deque雙端隊列的實現類來完成Satck的功能,這是因為java中Stack類設計的有一些問題,因為我們可以看到Satck是繼承自 Vector,但是依照程序設計的一個原則多組合少繼承來看,這個設計是不合理的,應該是Vector組成Satck比較好,然后java的官方也發現了這個問題,但是后面想更改的時候時間就比較晚了,所以才在文檔里面建議我們使用雙端隊列來模擬棧比較好。

  總結:Deque接口和它的實現提供了比Stack更完整的方法,以及也是FILO的原則,應優先使用此類。

 

2、Queue(單向隊列)

  在java中Queue是一個接口,在文檔當中有對其的描述如下:

  Queue除了基本的“收集”操作外,隊列還提供其他插入,提取和檢查操作。這些操作中的每一種都以兩種形式存在:一種在操作失敗時引發異常,另一種返回一個特殊值(根據操作為null或false)。插入操作的后一種形式是專為與容量受限的Queue實現一起使用而設計的。在大多數實現中,插入操作不會失敗。

  下面這個表格就對應着一個發生異常,一個返回特殊值:

  Throws exception Returns special value
Insert add(e) offer(e)
Remove remove() poll()
Examine element() peek()

  對應的方法介紹:

Modifier and Type Method and Description
boolean add(E e) :如果可以立即將指定的元素插入此隊列,而不會違反容量限制,則在成功時返回true,如果當前沒有可用空間,則拋出IllegalStateException。
E element() :檢索但不刪除此隊列的頭。
boolean offer(E e) :如果可以在不違反容量限制的情況下立即將指定的元素插入此隊列。
E peek() :檢索但不刪除此隊列的頭部,如果此隊列為空,則返回null。
E poll() :檢索並刪除此隊列的頭部,如果此隊列為空,則返回null。
E remove() :檢索並刪除此隊列的頭。

 

3、Deque(雙端隊列/棧)

  Deque是雙端隊列的接口,也是我們使用最多的隊列,既可以當作棧也可以當作隊列使用。

  Deque是支持在兩端插入和刪除元素的線性集合,雙端隊列是“雙端隊列”(double ended queue)的縮寫。大多數Deque對它們可能包含的元素數量沒有固定的限制,但是此接口支持容量受限的雙端隊列以及沒有固定大小限制的雙端隊列。此接口定義訪問雙端隊列兩端的元素的方,提供了用於插入,刪除和檢查元素的方法。這些方法中的每一種都以兩種形式存在:一種在操作失敗時引發異常,另一種返回一個特殊值(根據操作為null或false)。插入操作的后一種形式是專為容量受限的Deque實現而設計的。在大多數實現中,插入操作不會失敗。

  類似於隊列,它也有一個表格:

  First Element (Head)

  Throws exception Returns special value
Insert addFirst(e) offerFirst(e)
Remove removeFirst() pollFirst()
Examine getFirst() peekFirst()

  Last Element (Tail)

  Throws exception Returns special value
Insert addLast(e) offerLast(e)
Remove removeLast() pollLast()
Examine getLast() peekLast()

  因為該接口擴展了Queue接口,當雙端隊列用作隊列時,將導致FIFO(先進先出)行為。元素在雙端隊列的末尾添加,並從開頭刪除。從Queue接口繼承的方法與Deque方法完全等效,如下表所示:

Queue Method Equivalent Deque Method
add(e) addLast(e)
offer(e) offerLast(e)
remove() removeFirst()
poll() pollFirst()
element() getFirst()
peek() peekFirst()

  雙端隊列也可以用作LIFO(后進先出、先進后出)堆棧,此接口應優先於舊版Stack類使用。當雙端隊列用作堆棧時,元素從雙端隊列的開頭被壓入並彈出。堆棧方法與Deque方法完全等效,如下表所示:

Stack Method Equivalent Deque Method
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()

  總結來說,使用Deque作為隊列時主要使用隊列中的表對應提供的方法,而使用Deque作為棧時主要使用棧對應的表提供的方法。

  因為Deque繼承自Queue,所以Queue的方法本身在Deque中是起作用的,但是除此之外Deque中對應棧的方法如:push、pop也是有的,並且功能也是剛好對應。

   public void push(E e) {
    addFirst(e);
   }
 
   public E pop() {
    return removeFirst();
   }

  所以,總結來說,使用棧用Deque實現是完全沒有問題的。

  另外需要說明Deque的實現類有許多,但是我們平時用不考慮多線程,使用ArrayDeque、LinkedList就足夠了。

 

4、PriorityQueue(優先級隊列/堆)

  PriorityQueue是基於優先級堆的無界優先級隊列。優先級隊列的元素根據其自然順序或在隊列構造時提供的Comparator進行排序,具體取決於所使用的構造函數。優先級隊列不允許空元素。依賴自然順序的優先級隊列也不允許插入不可比較的對象,這樣做可能會導致ClassCastException

  簡單來說,PriorityQueue就是一個優先級隊列,在我們需要堆的時候可以使用PriorityQueue當作堆進行使用,因為PriorityQueue繼承自AbstractQueue,而AbstractQueue實現Queue,所以PriorityQueue的方法和Queue差不多,使用起來也比較方便。

 

5、隊列與棧的區別

5.1 數據的插入、刪除

5.1.1 棧

  是一種特殊的線性表,它只能在一段進行插入和刪除操作,就好像是一個井一樣。進行數據插入和刪除的一端類似於井口,稱為棧頂。而井也是有底部的,棧無法進行插入刪除操作的一端就被稱為棧底 棧中的數據好似往井里加水或抽水,加水永遠是加的離井口最近的水,同樣,抽水也是抽的離井口最近的水,這種特性也就是先進后出(FILO:First Input Last Output) 加水的操作就是進棧/壓棧/入棧,入的數據在棧的最頂端。 抽水的操作就是出棧,出的數據也在棧的頂端。

img

5.1.2 隊列

  隊列只允許在一端進行插入操作,另一端進行刪除操作。就像是排隊一樣,只能從后面排隊,在隊列的前面處理業務。而先排隊的人可以先進行業務處理,這也就是隊列的先進先出(FIFO:First Input First Output)原則。 隊列中的數據就是正在排隊的人,入隊列也就是加入排隊,而加入的那一邊稱為隊尾,出隊列也就是業務處理完成,離開排隊的地方,這一邊則被稱之為隊頭 img

5.2 遍歷速度

  :由於棧是先進后出,且只能在棧頂取出數據,而最先放入棧的數據最后才能被遍歷到,棧在遍歷時一般需要另外開辟空間來保證數據在遍歷時不會被打亂。

  隊列:隊列能基於地址指針進行遍歷,而且可以從隊頭/隊尾開始遍歷(不能兩邊同時遍歷),不需要另外開辟空間來保證數據的順序,不影響數據結構,因此速度要更快。

5.3 適用場景不同

  :具有記憶能力,使用於括號求解、表達式轉換、函數遞歸和調用的實現、深度優先搜索遍歷、瀏覽器后退功能等,需要記憶原來數據內容的場景。

  隊列:可以進行有順序的處理,如計算機系統中各種資源的管理、消息緩沖器的管理、廣度優先搜索等場景。

 

6、總結

在不考慮多線程的情況下

  • 使用棧就是使用Deque的實現類

  • 使用隊列就使用Deque的實現類

  • 使用堆就使用PriorityQueue。

 

參考鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM