最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java集合 ArrayDeque源碼詳細(xì)分析

 更新時(shí)間:2019年05月24日 11:07:01   作者:彤哥讀源碼  
ArrayDeque是一種以數(shù)組方式實(shí)現(xiàn)的雙端隊(duì)列,它是非線程安全的。下面小編和大家一起學(xué)習(xí)一下

問(wèn)題

(1)什么是雙端隊(duì)列?

(2)ArrayDeque是怎么實(shí)現(xiàn)雙端隊(duì)列的?

(3)ArrayDeque是線程安全的嗎?

(4)ArrayDeque是有界的嗎?

簡(jiǎn)介

雙端隊(duì)列是一種特殊的隊(duì)列,它的兩端都可以進(jìn)出元素,故而得名雙端隊(duì)列。

ArrayDeque是一種以數(shù)組方式實(shí)現(xiàn)的雙端隊(duì)列,它是非線程安全的。

繼承體系

通過(guò)繼承體系可以看,ArrayDeque實(shí)現(xiàn)了Deque接口,Deque接口繼承自Queue接口,它是對(duì)Queue的一種增強(qiáng)。

public interface Deque<E> extends Queue<E> {
 // 添加元素到隊(duì)列頭
 void addFirst(E e);
 // 添加元素到隊(duì)列尾
 void addLast(E e);
 // 添加元素到隊(duì)列頭
 boolean offerFirst(E e);
 // 添加元素到隊(duì)列尾
 boolean offerLast(E e);
 // 從隊(duì)列頭移除元素
 E removeFirst();
 // 從隊(duì)列尾移除元素
 E removeLast();
 // 從隊(duì)列頭移除元素
 E pollFirst();
 // 從隊(duì)列尾移除元素
 E pollLast();
 // 查看隊(duì)列頭元素
 E getFirst();
 // 查看隊(duì)列尾元素
 E getLast();
 // 查看隊(duì)列頭元素
 E peekFirst();
 // 查看隊(duì)列尾元素
 E peekLast();
 // 從隊(duì)列頭向后遍歷移除指定元素
 boolean removeFirstOccurrence(Object o);
 // 從隊(duì)列尾向前遍歷移除指定元素
 boolean removeLastOccurrence(Object o);
// *** 隊(duì)列中的方法 ***
 // 添加元素,等于addLast(e)
 boolean add(E e);
 // 添加元素,等于offerLast(e)
 boolean offer(E e);
 // 移除元素,等于removeFirst()
 E remove();
 // 移除元素,等于pollFirst()
 E poll();
 // 查看元素,等于getFirst()
 E element();
 // 查看元素,等于peekFirst()
 E peek();
// *** 棧方法 ***
// 入棧,等于addFirst(e)
 void push(E e);
 // 出棧,等于removeFirst()
 E pop();
// *** Collection中的方法 ***
 // 刪除指定元素,等于removeFirstOccurrence(o)
 boolean remove(Object o);
 // 檢查是否包含某個(gè)元素
 boolean contains(Object o);
 // 元素個(gè)數(shù)
 public int size();
 // 迭代器
 Iterator<E> iterator();
 // 反向迭代器
 Iterator<E> descendingIterator();
}

Deque中新增了以下幾類方法:

(1)*First,表示從隊(duì)列頭操作元素;

(2)*Last,表示從隊(duì)列尾操作元素;

(3)push(e),pop(),以棧的方式操作元素的方法;

源碼分析

主要屬性

// 存儲(chǔ)元素的數(shù)組
transient Object[] elements; // non-private to simplify nested class access
// 隊(duì)列頭位置
transient int head;
// 隊(duì)列尾位置
transient int tail;
// 最小初始容量
private static final int MIN_INITIAL_CAPACITY = 8;

從屬性我們可以看到,ArrayDeque使用數(shù)組存儲(chǔ)元素,并使用頭尾指針標(biāo)識(shí)隊(duì)列的頭和尾,其最小容量是8。

主要構(gòu)造方法

// 默認(rèn)構(gòu)造方法,初始容量為16
public ArrayDeque() {
 elements = new Object[16];
}
// 指定元素個(gè)數(shù)初始化
public ArrayDeque(int numElements) {
 allocateElements(numElements);
}
// 將集合c中的元素初始化到數(shù)組中
public ArrayDeque(Collection<? extends E> c) {
 allocateElements(c.size());
 addAll(c);
}
// 初始化數(shù)組
private void allocateElements(int numElements) {
 elements = new Object[calculateSize(numElements)];
}
// 計(jì)算容量,這段代碼的邏輯是算出大于numElements的最接近的2的n次方且不小于8
// 比如,3算出來(lái)是8,9算出來(lái)是16,33算出來(lái)是64
private static int calculateSize(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
 }
 return initialCapacity;
}

通過(guò)構(gòu)造方法,我們知道默認(rèn)初始容量是16,最小容量是8。

入隊(duì)

入隊(duì)有很多方法,我們這里主要分析兩個(gè),addFirst(e)和addLast(e)。

// 從隊(duì)列頭入隊(duì)
public void addFirst(E e) {
 // 不允許null元素
 if (e == null)
 throw new NullPointerException();
 // 將head指針減1并與數(shù)組長(zhǎng)度減1取模
 // 這是為了防止數(shù)組到頭了邊界溢出
 // 如果到頭了就從尾再向前
 // 相當(dāng)于循環(huán)利用數(shù)組
 elements[head = (head - 1) & (elements.length - 1)] = e;
 // 如果頭尾挨在一起了,就擴(kuò)容
 // 擴(kuò)容規(guī)則也很簡(jiǎn)單,直接兩倍
 if (head == tail)
 doubleCapacity();
}
// 從隊(duì)列尾入隊(duì)
public void addLast(E e) {
 // 不允許null元素
 if (e == null)
 throw new NullPointerException();
 // 在尾指針的位置放入元素
 // 可以看到tail指針指向的是隊(duì)列最后一個(gè)元素的下一個(gè)位置
 elements[tail] = e;
 // tail指針加1,如果到數(shù)組尾了就從頭開始
 if ( (tail = (tail + 1) & (elements.length - 1)) == head)
 doubleCapacity();
}

(1)入隊(duì)有兩種方式,從隊(duì)列頭或者從隊(duì)列尾;

(2)如果容量不夠了,直接擴(kuò)大為兩倍;

(3)通過(guò)取模的方式讓頭尾指針在數(shù)組范圍內(nèi)循環(huán);

(4)x & (len - 1) = x % len,使用&的方式更快;

擴(kuò)容

private void doubleCapacity() {
 assert head == tail;
 // 頭指針的位置
 int p = head;
 // 舊數(shù)組長(zhǎng)度
 int n = elements.length;
 // 頭指針離數(shù)組尾的距離
 int r = n - p; // number of elements to the right of p
 // 新長(zhǎng)度為舊長(zhǎng)度的兩倍
 int newCapacity = n << 1;
 // 判斷是否溢出
 if (newCapacity < 0)
 throw new IllegalStateException("Sorry, deque too big");
 // 新建新數(shù)組
 Object[] a = new Object[newCapacity];
 // 將舊數(shù)組head之后的元素拷貝到新數(shù)組中
 System.arraycopy(elements, p, a, 0, r);
 // 將舊數(shù)組下標(biāo)0到head之間的元素拷貝到新數(shù)組中
 System.arraycopy(elements, 0, a, r, p);
 // 賦值為新數(shù)組
 elements = a;
 // head指向0,tail指向舊數(shù)組長(zhǎng)度表示的位置
 head = 0;
 tail = n;
}

擴(kuò)容這里遷移元素可能有點(diǎn)繞,請(qǐng)看下面這張圖來(lái)理解。

出隊(duì)

出隊(duì)同樣有很多方法,我們主要看兩個(gè),pollFirst()和pollLast()。

// 從隊(duì)列頭出隊(duì)
public E pollFirst() {
 int h = head;
 @SuppressWarnings("unchecked")
 // 取隊(duì)列頭元素
 E result = (E) elements[h];
 // 如果隊(duì)列為空,就返回null
 if (result == null)
 return null;
 // 將隊(duì)列頭置為空
 elements[h] = null; // Must null out slot
 // 隊(duì)列頭指針右移一位
 head = (h + 1) & (elements.length - 1);
 // 返回取得的元素
 return result;
}
// 從隊(duì)列尾出隊(duì)
public E pollLast() {
 // 尾指針左移一位
 int t = (tail - 1) & (elements.length - 1);
 @SuppressWarnings("unchecked")
 // 取當(dāng)前尾指針處元素
 E result = (E) elements[t];
 // 如果隊(duì)列為空返回null
 if (result == null)
 return null;
 // 將當(dāng)前尾指針處置為空
 elements[t] = null;
 // tail指向新的尾指針處
 tail = t;
 // 返回取得的元素
 return result;
}

(1)出隊(duì)有兩種方式,從隊(duì)列頭或者從隊(duì)列尾;

(2)通過(guò)取模的方式讓頭尾指針在數(shù)組范圍內(nèi)循環(huán);

(3)出隊(duì)之后沒(méi)有縮容哈哈^^


前面我們介紹Deque的時(shí)候說(shuō)過(guò),Deque可以直接作為棧來(lái)使用,那么ArrayDeque是怎么實(shí)現(xiàn)的呢?

public void push(E e) {
 addFirst(e);
}

public E pop() {
 return removeFirst();
}

是不是很簡(jiǎn)單,入棧出棧只要都操作隊(duì)列頭就可以了。

總結(jié)

(1)ArrayDeque是采用數(shù)組方式實(shí)現(xiàn)的雙端隊(duì)列;

(2)ArrayDeque的出隊(duì)入隊(duì)是通過(guò)頭尾指針循環(huán)利用數(shù)組實(shí)現(xiàn)的;

(3)ArrayDeque容量不足時(shí)是會(huì)擴(kuò)容的,每次擴(kuò)容容量增加一倍;

(4)ArrayDeque可以直接作為棧使用;

彩蛋

雙端隊(duì)列與雙重隊(duì)列?

雙端隊(duì)列(Deque)是指隊(duì)列的兩端都可以進(jìn)出元素的隊(duì)列,里面存儲(chǔ)的是實(shí)實(shí)在在的元素。

雙重隊(duì)列(Dual Queue)是指一種隊(duì)列有兩種用途,里面的節(jié)點(diǎn)分為數(shù)據(jù)節(jié)點(diǎn)和非數(shù)據(jù)節(jié)點(diǎn),它是LinkedTransferQueue使用的數(shù)據(jù)結(jié)構(gòu)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java 中Map 的用法詳解

    Java 中Map 的用法詳解

    本文主要介紹java 中的Map 接口, 這里對(duì)Map 接口下的幾個(gè)類做了詳細(xì)介紹,希望對(duì)學(xué)習(xí)java 編程的小伙伴有所幫助
    2016-07-07
  • Flowable執(zhí)行完畢的流程查找方法

    Flowable執(zhí)行完畢的流程查找方法

    這篇文章主要為大家介紹了Flowable執(zhí)行完畢的流程查找方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Springboot如何通過(guò)filter修改Header的值

    Springboot如何通過(guò)filter修改Header的值

    這篇文章主要介紹了Springboot如何通過(guò)filter修改Header的值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • java字符串反轉(zhuǎn)示例分享

    java字符串反轉(zhuǎn)示例分享

    這篇文章主要介紹了將一個(gè)字符串進(jìn)行反轉(zhuǎn)或者字符串中指定部分進(jìn)行反轉(zhuǎn)的方法,大家參考使用吧
    2014-01-01
  • Spring框架實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼功能的代碼示例

    Spring框架實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼功能的代碼示例

    之前項(xiàng)目需要在驗(yàn)證碼模塊,增加滑動(dòng)驗(yàn)證碼,用來(lái)給手機(jī)端使用的,大概看了下,主要方法就是將圖片切割,然后記住偏移量,進(jìn)行滑動(dòng),所以本文給大家介紹了Spring框架實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼功能的方法示例,需要的朋友可以參考下
    2024-07-07
  • Java線程中sleep和wait的區(qū)別詳細(xì)介紹

    Java線程中sleep和wait的區(qū)別詳細(xì)介紹

    Java中的多線程是一種搶占式的機(jī)制,而不是分時(shí)機(jī)制。搶占式的機(jī)制是有多個(gè)線程處于可運(yùn)行狀態(tài),但是只有一個(gè)線程在運(yùn)行
    2012-11-11
  • Java實(shí)現(xiàn)實(shí)時(shí)視頻轉(zhuǎn)播的代碼示例

    Java實(shí)現(xiàn)實(shí)時(shí)視頻轉(zhuǎn)播的代碼示例

    這篇文章主要給大家詳細(xì)介紹了Java如何實(shí)現(xiàn)實(shí)時(shí)視頻轉(zhuǎn)播,文中通過(guò)代碼實(shí)例介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以自己動(dòng)手試一試
    2023-09-09
  • mybatis中一對(duì)一關(guān)系association標(biāo)簽的使用

    mybatis中一對(duì)一關(guān)系association標(biāo)簽的使用

    這篇文章主要介紹了mybatis中一對(duì)一關(guān)系association標(biāo)簽的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)

    Java 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)

    下面小編就為大家?guī)?lái)一篇Java 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • JVM調(diào)優(yōu)實(shí)戰(zhàn)

    JVM調(diào)優(yōu)實(shí)戰(zhàn)

    本文主要介紹了JVM調(diào)優(yōu)實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評(píng)論

台北市| 措美县| 平塘县| 铅山县| 东安县| 花垣县| 合山市| 辉南县| 丽江市| 县级市| 彝良县| 商水县| 黔南| 定西市| 东莞市| 章丘市| 涞源县| 南投县| 盐亭县| 舟山市| 大港区| 高要市| 邵武市| 贡觉县| 漳平市| 黎平县| 乐业县| 平昌县| 宁海县| 阿坝县| 平山县| 齐齐哈尔市| 辽宁省| 江陵县| 重庆市| 卢湾区| 通河县| 苍梧县| 屏山县| 阿瓦提县| 长宁区|