隊列(二):Deque

看書上講解OkHttp源碼時候,使用到了Deque,看朋友也都在用,這就有必要好好學習一心

轉自:
http://blog.csdn.net/buaaroid/article/details/51315860
http://blog.csdn.net/Buaaroid/article/details/51315970


說明:

一 隊列:

隊列是一種特殊的線性表,它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。隊列中沒有元素時,稱為空隊列。
這與我們生活中的隊列是一致的,前面消費,后邊插入

二 雙端隊列:

雙端隊列,顧名思義,兩端都能操作(操作一下嗎?),兩端都可以進行插入和刪除的操作,即:即可在表的前端,進行插入和刪除操作,又可以在表后端進行插入和刪除操作

三 栗子與應用

在OkHttp3.4.1的源碼中,看到了如下的應用

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;

  /** Executes calls. Created lazily. */
  private ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

可見,OkHttp,用ArrayDeque 作為了請求隊列,下面,我們來介紹ArrayDeque

1 繼承關系
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable

實現了Deque、Cloneable和Serializable接口。實現Cloneable和Serializable接口的主要目的是為了實現克隆和序列化,
繼承了AbstractCollection<E> 抽象類

2 成員變量:

ArrayDeque 類中,定義了4個成員變量,代碼如下:

 /**
     * The array in which the elements of the deque are stored.
     * The capacity of the deque is the length of this array, which is
     * always a power of two. The array is never allowed to become
     * full, except transiently within an addX method where it is
     * resized (see doubleCapacity) immediately upon becoming full,
     * thus avoiding head and tail wrapping around to equal each
     * other.  We also guarantee that all array cells not holding
     * deque elements are always null.
     */
    transient Object[] elements; // non-private to simplify nested class access

    /**
     * The index of the element at the head of the deque (which is the
     * element that would be removed by remove() or pop()); or an
     * arbitrary number equal to tail if the deque is empty.
     */
    transient int head;

    /**
     * The index at which the next element would be added to the tail
     * of the deque (via addLast(E), add(E), or push(E)).
     */
    transient int tail;

    /**
     * The minimum capacity that we'll use for a newly created deque.
     * Must be a power of 2.
     */
    private static final int MIN_INITIAL_CAPACITY = 8;
  • Object[] elements;
存儲deque元素的數組。
deque的容量是這個數組的長度,它總是2的冪。
該數組永遠不會變為滿,除了在addX方法中暫時調整大小(請參閱doubleCapacity),當它變為滿時,
從而避免頭部和尾部纏繞在彼此相等。
我們也保證所有不帶deque元素的數組單元都是空的。

簡言之: Object[] elements 存儲元素的 數組
  • transient int head;
在隊列head 處的元素的index(索引),即,接下來調用remove() 或 pop()方法來移除的元素的索引,
如果,對列為空,這個值 = tail的值
  • transient int tail;
接下來,調用addlast() 或者 add()方法,加入隊尾的元素的index(索引)
  • private static final int MIN_INITIAL_CAPACITY = 8;
我們將用于新創建的deque的最小容量。 必須是2的力量。

總結:
其中elements是元素集合數組,head是指向隊首的下標,tail是指向隊尾的下一個位置的下標。MIN_INITIAL_CAPACITY定義了在創建隊列是,如果有指定長度,而且指定長度小于MIN_INITIAL_CAPACITY,則使用MIN_INITIAL_CAPACITY作為數組的最小長度。

3 構造函數:

構造函數有3,

  • 1 無參構造:
  public ArrayDeque() {
        elements = new Object[16];
    }

默認元素,數組大小 16

  • 2 指定大小的構造:
    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }

調用了allocateElements(numElements)方法,

 private void allocateElements(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // Find the best power of two to hold elements.
        // Tests "<=" because arrays aren't kept full.
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;

            if (initialCapacity < 0)    // Too many elements, must back off
                initialCapacity >>>= 1; // Good luck allocating 2^30 elements
        }
        elements = new Object[initialCapacity];
    }

制定了:元素數組的大小,如果傳入的大小,小于MIN_INITIAL_CAPACITY,則使用MIN_INITIAL_CAPACITY作為數組的最小長度。

  • 2 指定元素構造:
    public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }

傳入一個元素集合,這個構造內部完成了:構造Object[] 數組,和填充元素的操作

4 常用方法:

這里要從Queue接口開始寫起:
轉自 : http://blog.csdn.net/Buaaroid/article/details/51315970

5 . Queue

在java5中新增加了Java.util.Queue接口,用以支持隊列的常見操作。該接口擴展了java.util.Collection接口。

public interface Queue<E> extends Collection<E> {
      //...
}

所以除了基本的Collection操作,隊列還支持:插入,提取和檢查操作

  • 類結構
img
img
:拋異常: :返回特殊值:
插入 add(e) offer(e)
移除 remove() pool()
檢查 element() peek()

隊列通常(但并非一定)以 FIFO(first in first out 先進先出)的方式排序各個元素。不過優先級隊列和 LIFO(last in first out 后進先出) 隊列(或堆棧)例外,優先級隊列根據提供的比較器或元素的自然順序對元素進行排序,LIFO隊列按 LIFO的方式對元素進行排序。

在 FIFO 隊列中,所有的新元素都插入隊列的末尾,移除元素從隊列頭部移除。

Queue使用時要盡量避免Collectionadd()remove()方法,而是要使用offer()來加入元素,使用poll()來獲取并移出元素。它們的優點是通過返回值可以判斷成功與否,add()和remove()方法在失敗的時候會拋出異常如果要使用前端而不移出該元素,使用element()或者peek()方法。

返回值 方法名 說明
增加
boolean add(E e) 是Collection接口里的方法,將指定元素插入此隊列的tail(如果立即可執行,且不會違反容量限制),在成功時返回true,如果當前沒有可用空間,則拋出IllegalStateException
boolean offer(E e) 是Queue接口里的方法,將指定元素插入此隊列的tail(如果立即可執行,且不會違反容量限制),返回true,否則返回false, 當使用有容量限制的隊列時,此方法通常要優于add(E),add(e)方法可能無法插入,拋出異常,
檢查
E element() 是Queue接口里的方法,返回head(隊頭),處的元素,但不移除此元素,如果隊列empty,則拋出異常
E peek() 是Queue接口里的方法,返回head(隊頭),處的元素,但不移除此元素,如果隊列empty,則返回null
移除
E remove() 是Queue接口里的方法,(注意,Collection里的remove(E e )方法,是有參的),獲取并移除此隊列的head,如果隊列empty,則拋出異常NoSuchElementException
E poll() 是Queue接口里的方法,(注意,Collection里的remove(E e )方法,是有參的),獲取并移除此隊列的head,如果隊列empty,則返回null

說明:

offer 方法可插入一個元素,否則返回 false。這與 Collection.add 方法不同,該方法只能通過拋出未經檢查的異常使添加元素失敗。

remove() 和 poll() 方法可移除和返回隊列的頭。到底從隊列中移除哪個元素是隊列排序策略的功能,而該策略在各種實現中是不同的。remove() 和 poll() 方法僅在隊列為空時其行為有所不同:remove() 方法拋出一個異常,而 poll() 方法則返回 null。

element() 和 peek() 返回,但不移除,隊列的頭。

Queue 實現通常不允許插入 null 元素,盡管某些實現(如 LinkedList)并不禁止插入 null。即使在允許 null 的實現中,也不應該將 null 插入到 Queue 中,因為 null 也用作 poll 方法的一個特殊返回值,表明隊列不包含元素。

值得注意的是LinkedList類實現了Queue接口,因此我們可以把LinkedList當成Queue來用。

 private void testDeque() {

        Deque<String> tmpDeque = new LinkedList<>();
        tmpDeque.offer("Hello");
        tmpDeque.offer("World");
        tmpDeque.offer("你好!");
        Log.d(TAG, "tmpDeque.size():" + tmpDeque.size());
        String str = null;
        while ((str = tmpDeque.poll()) != null) {
            Log.d(TAG, "str : " + str);
        }
        Log.d(TAG, "tmpDeque.size():" + tmpDeque.size());
    }
 D/DequeActivity: tmpDeque.size():3
 D/DequeActivity: str : Hello
 D/DequeActivity: str : World
 D/DequeActivity: str : 你好!
 D/DequeActivity: tmpDeque.size():0

6 . Deque

public interface Deque<E> extends Queue<E> {
          //....
}

Deque 接口繼承了Queue接口,所以支持Queue 的所有操作,間接也支持Collection的所以操作

類節構:

img

Deque是一個線性 collection,支持在兩端插入和移除元素。
名稱 deque 是“double ended queue(雙端隊列)”的縮寫,通常讀為“deck”。

大多數 Deque 實現對于它們能夠包含的元素數沒有固定限制,但此接口既支持有容量限制的雙端隊列,也支持沒有固定大小限制的雙端隊列。

img

此接口定義在雙端隊列兩端訪問元素的方法。提供插入、移除和檢查元素的方法。因為此接口繼承了隊列接口Queue,所以其每種方法也存在兩種形式:一種形式在操作失敗時拋出異常,另一種形式返回一個特殊值(null 或 false,具體取決于操作)。

  • 將Deque 用作單純的隊列(FIFO,尾進頭出,先進先出)

在將雙端隊列用作隊列時,將得到 FIFO(先進先出)行為。將元素添加到雙端隊列的末尾,從雙端隊列的開頭移除元素。從 Queue 接口繼承的方法完全等效于 Deque 方法,如下表所示:

Queue的方法 等效Deque的方法 描述
add(E e) addLast(E e) 添加至tail,失敗,則拋異常
offer() offerLast() 添加至tail,失敗,則返回false
remove() removeFirst() 移除head,失敗,則拋異常
poll() pollFirst() 移除head,失敗,返回null
element() getFirst() 檢查head,不移除,失敗,則拋異常
peek() peekFirst() 檢查head,,不移除,失敗,則返回null
img
  • 將Deque 用作棧(LIFO,頭進頭出,后進先出,比如activity的棧)

在將雙端隊列用作 LIFO(后進先出)堆棧。應優先使用此接口而不是遺留 Stack 類。在將雙端隊列用作堆棧時,元素被推入雙端隊列的開頭并從雙端隊列開頭彈出。堆棧方法完全等效于 Deque 方法,如下表所示:

堆棧方法 等效Deque的方法 描述
push(e) addFirst(e) 添加至頭部,失敗,則拋異常
offerFirst(e) 添加至頭部,失敗,則返回false
pop() removeFirst() 移除頭部,失敗,則拋異
pollFirst() 移除頭部,失敗,則返回false
peek() getFirst() 檢查頭部,失敗,則拋異常
peekFirst() 檢查頭部,失敗,則返回null
img
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容