国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java ArrayDeque源碼剖析

2019-11-06 07:26:00
字體:
來源:轉載
供稿:網友

引用自此博客文章,感謝CarpenterLee

java ArrayDeque源碼剖析

本文github地址

前言

Java里有一個叫做Stack的類,卻沒有叫做Queue的類(它是個接口名字)。當需要使用棧時,Java已不推薦使用Stack,而是推薦使用更高效的ArrayDeque;既然Queue只是一個接口,當需要使用隊列時也就首選ArrayDeque了(次選是LinkedList)。

總體介紹

要講棧和隊列,首先要講Deque接口。Deque的含義是“double ended queue”,即雙端隊列,它既可以當作棧使用,也可以當作隊列使用。下表列出了DequeQueue相對應的接口:

Deque與Queue對應接口

下表列出了Deque與Stack對應的接口:

Deque與Stack對應接口

上面兩個表共定義了Deque的12個接口。添加,刪除,取值都有兩套接口,它們功能相同,區(qū)別是對失敗情況的處理不同。一套接口遇到失敗就會拋出異常,另一套遇到失敗會返回特殊值(falsenull)。除非某種實現(xiàn)對容量有限制,大多數情況下,添加操作是不會失敗的。雖然Deque的接口有12個之多,但無非就是對容器的兩端進行操作,或添加,或刪除,或查看。明白了這一點講解起來就會非常簡單。

ArrayDequeLinkedListDeque的兩個通用實現(xiàn),由于官方更推薦使用AarryDeque用作棧和隊列,加之上一篇已經講解過LinkedList,本文將著重講解ArrayDeque的具體實現(xiàn)。

從名字可以看出ArrayDeque底層通過數組實現(xiàn),為了滿足可以同時在數組兩端插入或刪除元素的需求,該數組還必須是循環(huán)的,即循環(huán)數組(circular array),也就是說數組的任何一點都可能被看作起點或者終點。ArrayDeque是非線程安全的(not thread-safe),當多個線程同時使用的時候,需要程序員手動同步;另外,該容器不允許放入null元素。

ArrayDeque

上圖中我們看到,head指向首端第一個有效元素,tail指向尾端第一個可以插入元素的空位。因為是循環(huán)數組,所以head不一定總等于0,tail也不一定總是比head大。

方法剖析

addFirst()

addFirst(E e)的作用是在Deque的首端插入元素,也就是在head的前面插入元素,在空間足夠且下標沒有越界的情況下,只需要將elements[--head] = e即可。

addFirst()

實際需要考慮:1.空間是否夠用,以及2.下標是否越界的問題。上圖中,如果head為0之后接著調用addFirst(),雖然空余空間還夠用,但head-1,下標越界了。下列代碼很好的解決了這兩個問題。

//addFirst(E e)public void addFirst(E e) { if (e == null)//不允許放入null throw new NullPointerException(); elements[head = (head - 1) & (elements.length - 1)] = e;//2.下標是否越界 if (head == tail)//1.空間是否夠用 doubleCapacity();//擴容}

上述代碼我們看到,空間問題是在插入之后解決的,因為tail總是指向下一個可插入的空位,也就意味著elements數組至少有一個空位,所以插入元素的時候不用考慮空間問題。

下標越界的處理解決起來非常簡單,head = (head - 1) & (elements.length - 1)就可以了,這段代碼相當于取余,同時解決了head為負值的情況。因為elements.length必需是2的指數倍,elements - 1就是二進制低位全1,跟head - 1相與之后就起到了取模的作用,如果head - 1為負數(其實只可能是-1),則相當于對其取相對于elements.length的補碼。

下面再說說擴容函數doubleCapacity(),其邏輯是申請一個更大的數組(原數組的兩倍),然后將原數組復制過去。過程如下圖所示:

doubleCapacity()

圖中我們看到,復制分兩次進行,第一次復制head右邊的元素,第二次復制head左邊的元素。

//doubleCapacity()PRivate void doubleCapacity() { assert head == tail; int p = head; int n = elements.length; int r = n - p; // head右邊元素的個數 int newCapacity = n << 1;//原空間的2倍 if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r);//復制右半部分,對應上圖中綠色部分 System.arraycopy(elements, 0, a, r, p);//復制左半部分,對應上圖中灰色部分 elements = (E[])a; head = 0; tail = n;}

addLast()

addLast(E e)的作用是在Deque的尾端插入元素,也就是在tail的位置插入元素,由于tail總是指向下一個可以插入的空位,因此只需要elements[tail] = e;即可。插入完成后再檢查空間,如果空間已經用光,則調用doubleCapacity()進行擴容。

addLast()

public void addLast(E e) { if (e == null)//不允許放入null throw new NullPointerException(); elements[tail] = e;//賦值 if ( (tail = (tail + 1) & (elements.length - 1)) == head)//下標越界處理 doubleCapacity();//擴容}

下標越界處理方式addFirt()中已經講過,不再贅述。

pollFirst()

pollFirst()的作用是刪除并返回Deque首端元素,也即是head位置處的元素。如果容器不空,只需要直接返回elements[head]即可,當然還需要處理下標的問題。由于ArrayDeque中不允許放入null,當elements[head] == null時,意味著容器為空。

public E pollFirst() { E result = elements[head]; if (result == null)//null值意味著deque為空 return null; elements[h] = null;//let GC work head = (head + 1) & (elements.length - 1);//下標越界處理 return result;}

pollLast()

pollLast()的作用是刪除并返回Deque尾端元素,也即是tail位置前面的那個元素。

public E pollLast() { int t = (tail - 1) & (elements.length - 1);//tail的上一個位置是最后一個元素 E result = elements[t]; if (result == null)//null值意味著deque為空 return null; elements[t] = null;//let GC work tail = t; return result;}

peekFirst()

peekFirst()的作用是返回但不刪除Deque首端元素,也即是head位置處的元素,直接返回elements[head]即可。

public E peekFirst() { return elements[head]; // elements[head] is null if deque empty}

peekLast()

peekLast()的作用是返回但不刪除Deque尾端元素,也即是tail位置前面的那個元素。

public E peekLast() { return elements[(tail - 1) & (elements.length - 1)];}
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 衡南县| 灵寿县| 彰化市| 巧家县| 安化县| 乌鲁木齐县| 晋中市| 南汇区| 富蕴县| 仁布县| 古田县| 克拉玛依市| 扶沟县| 莫力| 泰顺县| 威宁| 腾冲县| 宁城县| 容城县| 绥芬河市| 娄底市| 龙陵县| 盐源县| 宁明县| 白玉县| 新干县| 鹤岗市| 福鼎市| 太原市| 巴中市| 南通市| 邵东县| 攀枝花市| 濮阳市| 遵义县| 吉木萨尔县| 金门县| 沙河市| 咸丰县| 稻城县| 仁寿县|