Queue與Deque的區別


前言

​ 在研究java集合源碼的時候,發現了一個很少用但是很有趣的點:Queue以及Deque,平常在寫leetcode經常用LinkedList向上轉型Deque作為棧或者隊列使用,但是一直都不知道Queue的作用,於是就直接官方文檔好了。

正文

概念


從上圖看出,Queue以及Deque都是繼承於Collection,Deque是Queue的子接口。
下面來看一下官方文檔的解釋。

A linear collection that supports element insertion and removal at both ends. The name deque is short for "double ended queue" and is usually pronounced "deck". Most Deque implementations place no fixed limits on the number of elements they may contain, but this interface supports capacity-restricted deques as well as those with no fixed size limit.

A collection designed for holding elements prior to processing. Besides basic Collection operations, queues provide additional insertion, extraction, and inspection operations. Each of these methods exists in two forms: one throws an exception if the operation fails, the other returns a special value (either null or false, depending on the operation). The latter form of the insert operation is designed specifically for use with capacity-restricted Queue implementations; in most implementations, insert operations cannot fail.

從Deque的解釋中,我們可以得知:Deque是double ended queue,我將其理解成雙端結束的隊列,雙端隊列,可以在首尾插入或刪除元素。而Queue的解釋中,Queue就是簡單的FIFO隊列。
所以在概念上來說,Queue是FIFO的單端隊列,Deque是雙端隊列。
而在使用上,又有什么差別呢?

使用

從上圖我們可以得知,Queue有一個直接子類PriorityQueue,而Deque中直接子類有兩個:LinkedList以及ArrayDeque。

  • PriorityQueue

我覺得重點就在圈定的兩個單詞:無邊界的,優先級的堆。然后再看看源碼


在第一張圖片的源碼中,明顯看到PriorityQueue的底層數據結構是數組,而無邊界的形容,那么指明了PriorityQueue是自帶擴容機制的,具體請看PriorityQueue的grow方法。
在第二張第三張圖片中,可以看到插入元素的時候是需要經過compareTo的處理,那么最常用就是一些范圍極值的輸出,類似於堆排序的用法。

下面演示一下正反序輸出三個元素的使用

private static void negativePrint(int[] nums) {
        PriorityQueue<Integer> queue=new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        for(int temp:nums){
            queue.add(temp);
        }
        System.out.println();
        System.out.print("倒序輸出:");
        for(int i=0;i<3;i++){
            System.out.print(queue.poll()+" ");
        }
    }

    private static void positivePrint(int[] nums){
        PriorityQueue<Integer> queue=new PriorityQueue<>();
        for(int temp:nums){
            queue.add(temp);
        }
        System.out.print("正序輸出:");
        for(int i=0;i<3;i++){
            System.out.print(queue.poll()+" ");
        }
    }
正序輸出:1 2 3 
倒序輸出:9 8 8 

這個在一些排行榜或者輸入第N個最大/小元素會比較常用。

  • LinkedList以及ArrayDeque

從官方解釋來看,ArrayDeque是無初始容量的雙端隊列,LinkedList則是雙向鏈表。而我們還能看到,ArrayDeque作為隊列時的效率比LinkedList要高,而在棧的使用場景下,無疑具有尾結點不需判空的LinkedList較高效。
下面演示ArrayDeque作為隊列以及LinkedList作為棧的使用

private static void usingAsQueue() {
        Deque<Integer> queue=new ArrayDeque<>();
        System.out.println("隊列為空:"+queue.isEmpty());   //判斷隊列是否為空
        queue.addLast(12);   //添加元素
        System.out.println("隊列為空:"+queue.isEmpty());   //判斷隊列是否為空
        System.out.println(queue.peekFirst());   //獲取隊列首部元素
        System.out.println(queue.pollFirst());   //獲取並移除棧頂元素
        System.out.println("隊列為空:"+queue.isEmpty());   //判斷隊列是否為空
 }

private static void usingAsStack() {
        //作為棧使用
        Deque<Integer> stack=new LinkedList<>();
        System.out.println("棧為空:"+stack.isEmpty());   //判斷棧是否為空
        stack.addFirst(12);
        System.out.println("棧為空:"+stack.isEmpty());   //判斷棧是否為空
        System.out.println(stack.peekFirst());   //獲取棧頂元素
        System.out.println(stack.pollFirst());   //獲取並移除棧頂元素
        System.out.println("棧為空:"+stack.isEmpty());   //判斷棧是否為空
        System.out.println("============================================");
    }

棧為空:true
棧為空:false
12
12

棧為空:true

隊列為空:true
隊列為空:false
12
12
隊列為空:true

小提示

在Deque中,獲取並移除元素的方法有兩個,分別是removeXxx以及peekXxx。

存在元素時,兩者的處理都是一樣的。但是當Deque內為空時,removeXxx會直接拋出NoSuchElementException,而peekXxx則會返回null。

所以無論在實際開發或者算法時,推薦使用peekXxx方法

其實ArrayDeque和LinkedList都可以作為棧以及隊列使用,但是從執行效率來說,ArrayDeque作為隊列以及LinkedList作為棧使用會是更好的選擇。
另外,我在leetcode看到有人采用Vector下的Stack,這個同步加鎖粒度過大(對象級),另外我覺得算法中沒有線程同步的需要吧。

  • 小結

PriorityQueue可以作為堆使用,而且可以根據傳入的Comparator實現大小的調整,會是一個很好的選擇。
ArrayDeque通常作為棧或隊列使用,但是棧的效率不如LinkedList高。
LinkedList通常作為棧或隊列使用,但是隊列的效率不如ArrayQueue高。

總結

在java中,Queue被定義成單端隊列使用,Deque被定義成雙端隊列使用。
而由於雙端隊列的定義,Deque可以作為棧或者隊列使用,而Queue只能作為隊列或者依賴於子類的實現作為堆使用。

本文首發於cartoon的博客
轉載請注明出處:https://cartoonyu.github.io/cartoon-blog/post/java/queue%E4%B8%8Edeque%E7%9A%84%E5%8C%BA%E5%88%AB/


免責聲明!

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



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