Java 集合框架(上):List薪夕、Set 和 Queue

Java 集合框架(上)

友情提示:文章可能有點長脚草,各種集合的實現(xiàn)原理我用自己的理解去解釋了,配合源碼食用更佳原献。

一馏慨、Java集合框架簡述

簡介:上古時期(Java 2之前) Java 就提供的數據操作類:Dictionary、Vector姑隅、Stack 等写隶,但是它們存在著效率較低、缺少統(tǒng)一的主題的問題讲仰。為了解決這些問題慕趴,Java 重新進行了設計,就是現(xiàn)在的 Java集合框架鄙陡。

java 集合框架(圖片來自網絡)

從上圖可以看出冕房,Java 集合框架主要包含三個方面:

  • 迭代器(Iterator):提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節(jié)柔吼。
  • 集合(Collection):用來儲存元素集合毒费,列表等丙唧。
    Collection 包括 List愈魏、Set 和 Queue。這些抽象是為了定義通用方法和變量想际,統(tǒng)一管理抽象和實現(xiàn)類等培漏。常用實現(xiàn)類有 HashSet、LinkedHashSet胡本、ArrayList 等牌柄。
  • 圖(Map):用于儲存鍵值對。
    Map 包括 AbstractMap 和 SortedMap 等抽象侧甫,原理與上面相同珊佣。再往下是 HashMap蹋宦、LinkedHashMap 等具體實現(xiàn)。

二咒锻、集合框架的構成

2.1 接口 Iterable

說明

  • 是 Java 集合框架的頂級接口冷冗。
  • 實現(xiàn)此接口,定義自己的迭代器實現(xiàn)對數據容器的遍歷惑艇。

Iterable 三個成員方法:

Iterator<T> iterator(); 返回一個迭代器蒿辙。

void forEach(Consumer<? super T> action):對內部元素進行遍歷,并對元素進行指定的操作

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

pliterator<T> spliterator() :創(chuàng)建并返回一個可分割迭代器

default Spliterator<T> spliterator() {
    return Spliterators.spliteratorUnknownSize(iterator(), 0);
}

那么回頭看第一個成員方法滨巴,返回的是一個迭代器思灌,這個迭代器也是一個接口,里面定義了常用的一些方法:

接口 Iterator :迭代器

public interface Iterator<E> {
    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
  • hasNext():是否存在下一個元素恭取。
  • next():返回下一個元素泰偿。
  • remove():移除一個元素(迭代器實現(xiàn),與 List 的 remove() 不同)蜈垮。

也就是說甜奄,集合框架中幾乎所有容器都實現(xiàn)了 Iterable,通過它們的 iterator() 方法獲取自己定義的實現(xiàn)了 Iterator 的迭代器窃款。

舉個栗子:

public class IterableTest {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next());
        }
    }
    
}

比較簡單课兄,首先調用 list.iterator() 獲取 Iterator 實例,這里的 list 是一個 ArrayList晨继,所以獲取的就是 ArrayList 的迭代器實現(xiàn)烟阐。
那么看一下 ArrayList 的迭代器是怎樣的:

ArrayList # iterator()方法 和 內部迭代器 Itr

public Iterator<E> iterator() {
        return new Itr();
    }

private class Itr implements Iterator<E> {
    protected int limit = ArrayList.this.size;

    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor < limit;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        ...
        return (E) elementData[lastRet = i];
    }

    public void remove() {
            ...
        try {
            ArrayList.this.remove(lastRet);
            ...
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        ...
    }
}

可以看到 ArrayList 的 iterator() 創(chuàng)建自定義迭代器 Itr,Itr 是一個 Iterator 實現(xiàn)紊扬,實現(xiàn)了 hasNext()蜒茄、next() 等主要方法。

上面遍歷 ArrayList 的小栗子就是通過調用它的迭代器相關方法來實現(xiàn)的餐屎。

特別注意

你可能注意到了檀葛,在描述 Iterator 的 remove() 方法時特意加了括號表面與 List 的 remove() 方法的不同。

那么在使用迭代器進行遍歷需要刪除元素時需要調用迭代器的 remove() 方法腹缩,調用 list 的 remove() 會造成并行修改異常(ConcurrentModificationException)屿聋。

錯誤示范:

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
    String s = iterator.next();
    if(s.equals("b")){
        list.remove(s);
    }
}

正確寫法應為:

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
    String s = iterator.next();
    if(s.equals("b")){
        iterator.remove();
    }
}

自己實現(xiàn)迭代器

如果我們想自己寫一個集合并使用迭代器來遍歷的話,可以這樣寫:

public class MyList<E> implements Iterable<E> {

    @Override
    public Iterator<E> iterator() {
        return new MyIterator();
    }

    private class MyIterator implements Iterator<E>{

        @Override
        public boolean hasNext() {
            return false;
        }

        @Override
        public E next() {
            return null;
        }

        @Override
        public void remove() {
        }
    }
}

2.2 接口 Collection

Collection 接口包含的方法藏鹊,AS 找到這個類润讥,快捷鍵 Ctrl + O:


Collection 接口方法.png

可見 Collection 定義的這些方法有一些是我們經常用到的,比如 add() 添加元素盘寡,clear() 刪除所有元素楚殿。

正是因為 Collection 定義了這些接口,所有的實現(xiàn)類都受到該接口的管理竿痰。這里是接口的一個功能的體現(xiàn): 定義一個規(guī)范(協(xié)議)脆粥,這樣做的好處是 Collection 無需知道子類是如何實現(xiàn)的砌溺,而且擴展性高、解耦变隔。

接口的三大作用:實現(xiàn)多繼承抚吠、定義一個規(guī)范(協(xié)議),用于回調弟胀。

先看圖片的左邊部分:

AbstractCollection:實現(xiàn)了大部分的集合接口楷力。

2.2.1 接口 List extends Collection

List

特點:

  • List 繼承了 Collection,它定義一個 允許重復的有序集合孵户。
  • 因為是有序集合萧朝,所以查找元素效率高
  • 同時因為刪除和插入數據會引起其它元素位置改變夏哭,所以刪除和插入數據效率低检柬。

從實現(xiàn)方法來看,List 增加了允許在指定位置竖配,也就是下標操作元素的功能何址。同時增加了 ListIterator 迭代器,它是迭代器接口 Iterator 的子類进胯,主要用于定義雙向遍歷的功能用爪,具體由子類實現(xiàn)。

AbstractList extends AbstractCollection implements List:繼承于AbstractCollection 并且實現(xiàn)了大部分List接口胁镐,抽象類偎血。

AbstractSequentialList<E> extends AbstractList<E>:AbstractSequentialList 繼承 AbstractList,也是一個抽象類盯漂,用于提供了對元素鏈式訪問而不是隨機訪問颇玷。

ArrayList

ArrayList

特點:

  • 數組實現(xiàn),大小可變就缆。
  • 根據下標獲取元素帖渠,隨機訪問和遍歷元素時提供更好的性能。新增和移除元素時效率較低竭宰。
  • 非同步空郊。

ArrayList 繼承了 AbstractList 且實現(xiàn) List,此外還實現(xiàn)了 Serializable羞延,說明它支持序列化渣淳,可用于傳輸和儲存相關。下面記錄 ArrayList 常用方法的實現(xiàn):

ArrayList # add():添加元素

//1.定義 Object 數組
transient Object[] elementData;

public boolean add(E e) {
    //2.確定和記錄數組容量
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //5.將新元素添加到新數組
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 3.確定容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //4.復制原有數據到新數組
    elementData = Arrays.copyOf(elementData, newCapacity);
}

看注釋序號:

  1. 定義 Object 數組:定義這個數組用于儲存 ArrayList 中的數據伴箩,transient 修飾的變量不參與序列化,但是 ArrayList 實際使用時數據是可以進行序列化的鄙漏。原因可以參考下面:

用transient修飾的成員變量不能序列化嗤谚,為什么ArrayList集合可以實現(xiàn)序列化

也就是說 ArrayList 提供了自己的 writeObject() 方法所以可以進行序列化棺蛛,但是為什么不直接使用 Object[] elementData呢?原因是 ArrayList 是動態(tài)數組可以自增長容量巩步,但是如果設定了數組容量是 100旁赊,而實際只放了一個元素,那么默認序列化后就會生成 99 個 null 元素椅野,這明顯是沒必要的终畅。所以 ArrayList 就提供了自己的序列化方法 writeObject() 在序列化時只遍歷實際元素,這樣就避免了指定長度生成無用元素的弊端竟闪。

以上答案參考:

ArrayList如何實現(xiàn)序列化离福?

  1. 確定和記錄數組容量:ensureCapacityInternal()ensureCapacityInternal() 經過計算得出要創(chuàng)建的數組大小,這個容量用于創(chuàng)建新數組炼蛤。
  2. 新的容量為舊的容量加上它的一半妖爷,也就是說每次擴容會增加原來的 50%。
    *這里的 ">>" 是移位預算符理朋,表示二進制的向右移動一位絮识,獲得的結果是原來的一半,反之則是原來的一倍嗽上。比如:4>>1 = 2次舌。那么 4<<1 = 8。
  3. 復制原有數據到新數組:利用 copyOf() 方法創(chuàng)建一個新數組兽愤,且容量已確定垃它,同時將原有數據復制給新的數組。
  4. 將新元素添加到新數組:elementData[size++] = e; 實現(xiàn)了將新元素的添加并且下標加一烹看。

除了上面添加元素的方法国拇,其它常用的比如 indexOf() 其實是遍歷數組 elementData 中的所有元素并進行對比,如果存在相同元素則返回惯殊,反之返回 -1.

ArrayList # 其他主要方法

方法名 作用 重載/說明
add(E e) 添加元素 add(int index, E element) 指定位置添加元素
addAll(Collection<? extends E> c) 添加整個其它集合 (int index, Collection<? extends E> c) 指定位置添加整個集合
clear() 清除集合 -
contains(Object o) 判斷是否存在該元素 遍歷實現(xiàn)
remove(int index) 移除元素 remove(Object o) Object 參數的重載
subList(int fromIndex, int toIndex) 返回從 fromIndex 到 toIndex 的列表 注意:返回的是原列表酱吝,進行修改原列表這段數據也會更改
ensureCapacity(int minCapacity) 對底層數組進行擴容。 在確定要插入數據的數量時土思,首先調用此方法進行擴容务热,避免每次添加時拷貝數組,提高效率己儒。
ensureCapacityInternal(int minCapacity) 同上 只供 ArrayList 內部使用的增加容量的方法崎岂,jdk1.7 加入。
trimToSize() 去除 ArrayList 申請的多余儲存空間闪湾。 ArrayList 每次自增原來的 50%冲甘,這就造成有一部分未使用的空間,該方法可以去除未使用的空間。
LinkedList

LinkedList

特點:

  • 底層是鏈表實現(xiàn)江醇。
  • 允許有 null(空)元素濒憋,主要用于創(chuàng)建鏈表數據結構。
  • LinkedList 是一個雙鏈表陶夜,所以在添加和刪除數據時比 ArrayList 有更好的性能凛驮,但查找效率低。
  • 非同步条辟∏玻可使用下列方法實現(xiàn)同步:

Listlist=Collections.synchronizedList(newLinkedList(...));

了解關于鏈表可參考我另一篇筆記:

Java 數據結構了解一下

屬性:LinkedList 有三個成員變量:

int size = 0; // LinkedList 長度
Node<E> first; // 首節(jié)點
Node<E> last; // 尾結點

方法:挑幾個重要的方法記錄

LinkedList # add(E e):添加元素

public boolean add(E e) {
    linkLast(e);
    return true;
}

/**
 * Links e as last element.
 */
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

直接將新增的元素放到鏈表的最后面,并將 LinkedList 長度加 1羽嫡,修改的次數(modCount)加1本姥。

LinkedList # add(int index, E element):添加元素到指定位置

public void add(int index, E element) {
    // 1. 檢測 index 是否合法
    checkPositionIndex(index);

    // 2. 判斷元素插入位置
    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    final Node<E> pred = succ.prev;
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    // 3. 插入到當前元素之前
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}
  1. 首先進行檢測,查看 index 是否大于等于 0 和 小于等于數組長度厂僧,如果不合法則拋出(這里代碼比較簡單扣草,省略掉了)。
  2. 進行判斷颜屠,如果插入到末尾辰妙,直接調用剛才也用到的 linkLast() 方法就好。反之調用 linkBefore() 把要插入的元素放到下標元素之前甫窟。
  3. 如果下標元素的前一個元素為空密浑,則說明下標元素已經在隊首了,因為之前已經把下標元素的前元素指向新元素粗井,所以直接把新元素放到隊首即可尔破。最后進行 LinkedList 的長度(size)和修改次數(modCount)的增長。

LinkedList # get(int index):根據下標獲取元素浇衬,該方法是通過遍歷來獲取的

public E get(int index) {
    // 1.判斷
    checkElementIndex(index);
    return node(index).item;
}

Node<E> node(int index) {
    // 2. index 和鏈表長度的一半進行對比
    if (index < (size >> 1)) { //2.1 小于一半
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {//2.2 大于一半
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
  1. 依舊是判斷下標合法性懒构。
  2. 下標和數組長度一半進行判斷。因為雙鏈表的特性耘擂,所以不需要進行全部元素的遍歷胆剧。
    2.1 如果 index 小于鏈表長度的一半,只需遍歷鏈表的前半部分醉冤,遍歷到第 index-1 個元素的下一個元素就是需要的元素秩霍。
    2.2 如果 index 大于鏈表的一半,遍歷后半部分蚁阳,拿前一個結點即可铃绒。

LinkedList # remove(int index):根據下標移除元素,移除的過程就是重新指向和值置空的過程:

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;
    // 1. 判斷是否是首元素
    if (prev == null) {
        first = next;
    } else {// 1.1 不是首元素
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }
    // 2.當前元素的值置空
    x.item = null;
    size--;
    modCount++;
    return element;
}

首先依舊判斷要移除的元素 item 是否合法螺捐。

  1. 如果當前元素的前一個元素為 null颠悬,說明它是首元素矮燎。直接將首元素由下一個元素覆蓋,就完成了移除過程椿疗。
    1.1 不是首元素則由下一個元素覆蓋當前元素漏峰,并把 x 的一個元素置空糠悼。因為 x 已經被替換了届榄,新來的有自己的 prev,所以也無需保存 x 的 prev 了倔喂。
  2. 把要移除的元素的值置空铝条,然后長度減 1,修改次數加 1席噩。

LinkedList # 其他主要方法

方法名 作用 重載/說明
add(E e) 添加元素 add(int index, E element):相應下標添加元素班缰,屬于 List 的方法
offer(E e) 添加元素 不違反容量限制的情況下,將元素插入到 Queue 中悼枢,屬于 Queue 的方法
addAll(Collection<? extends E> c) 添加整個集合 addAll(int index, Collection<? extends E> c) 指定位置添加整個集合
clear() 清除所有元素 -
contains(Object o) 判斷是否包含該元素 先遍歷前半段埠忘,后遍歷后半段
get(int index) 根據下標獲取元素 也是分段遍歷忱详,找到就返回
set(int index, E element) 更新某結點元素 -
remove(int index) 根據下標移除元素 -
toArray() 轉換為 Object 數組 遍歷鏈表結點數據廊遍,放到 Object[] 最后返回
Vector

Vector

特點:

  • 同步:基本所有 public 方法或者其具體操作都添加同步關鍵字 synchronized搁吓。
  • 效率較低:因為基本所有方法都是同步的饰潜,所以會造成性能損失粪狼。但其效率問題不止是因為這一個原因呈驶。
  • 不推薦使用(...):可使用 Collections 的 synchronizedList 方法同步 ArrayList 來替代捂掰。
Stack extends Vector

繼承 Vector 實現(xiàn)的棧羡鸥。

主要方法有三:

  • push(E item):添加元素蜈块。
  • peek():返回棧頂元素鉴腻,不移除。
  • pop():返回棧頂元素并移除百揭。

棧的實現(xiàn)方式也有很多爽哎,參考筆記里面有關棧的記錄:

Java 數據結構了解一下

2.2.2 接口 Set extends Collection

Set

Set 集合三大特征:確定性、互斥性器一、無序性课锌。大部分實現(xiàn)此接口的集合都具有此特性。

特點:

  • 強調元素無序性盹舞,但是不可重復产镐,重復的元素會被覆蓋掉。
  • 檢索元素效率較低踢步,插入和刪除效率高且不會引起元素位置改變。

抽象類 AbstractSet

繼承了 AbstractCollection获印,實現(xiàn)了 Set 的大部分接口。這個抽象類就是為了方便擴展而存在的。

HashSet

HashSet

特點:

  • 底層實現(xiàn)是 HashMap唆缴。
  • 不允許出現(xiàn)重復元素面徽,且不保證順序。因為它的數據存儲是用 HashMap 實現(xiàn)的趟紊。
  • 允許包含值為 null 的元素碰酝,但最多只能一個。

HashSet # add(E e)

private transient HashMap<E,Object> map;

private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

這里涉及到 HashMap 的特性:map 儲存時如果當前 map 沒有相同的 key铛嘱,則返回 null袭厂。
也就是說只有當新添加的值 e 為新的 key 時,才會返回 null嵌器,從而判定成功使 Set 加入這個值肛真。
所以,當 e 為 null 時也可以存進 map(HashMap key爽航、value 均可以為 null)蚓让,但是只能存一個 null,多了添加時會返回 false 存不進去讥珍。

HashSet # remove(Object o)

public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

remove 的實現(xiàn)也比較簡單历极,調用內部 map 的 remove() 方法。返回的是被移除的 map 的值(猜的)衷佃,如果該值與儲存的 PRESENT 相同則表明從 Set 移除成功趟卸。

HashSet 遍歷

public static void main(String[] args) {
    Set<String> set = new HashSet<String>();

    set.add("aaa");
    set.add("bbb");
    set.add("ccc");
    set.add("ccc"); //故意加一個相同元素,遍歷出來確實沒它氏义。

    //遍歷
    Iterator iterator = set.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }

    //或者這樣
    for (String s:set) {
        System.out.println(s);
    }
}
LinkedHashSet

LinkedHashSet

特點:

  • 底層是 LinkedHashMap 實現(xiàn)锄列。
  • 繼承 HashSet,但是是鏈表實現(xiàn)惯悠,所以可以保證元素順序邻邮。
  • 有序,允許為 null克婶。
  • 非線程安全筒严,底層操作通過 LinkedHashMap 實現(xiàn)丹泉。

HashSet 構造函數

public LinkedHashSet() {
    super(16, .75f, true);
}

public LinkedHashSet(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor, true);
}

HashSet 調用了父類 HashSet 的構造函數:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
  1. 第一個參數 initialCapacity 表示初始容量,可以看到默認的 LinkedHashSet 初始容易為 16鸭蛙。
  2. 第二個參數 loadFactor 表示裝載因子摹恨。比如這個值為 0.5,則每次容量達到一半時進行擴容(默認 16 達到 8 擴容為 32)娶视。那么當這個值為 0.75晒哄,當容量達到當前最大容量的 75% 時進行擴容揩晴。
    為啥是 0.75 呢?哈哈寒锚,想笑刹前。

HashMap的loadFactor為什么是0.75?

  1. 第三個參數作為標記與 HashSet 構造方法區(qū)分拣技。
TreeSet

TreeSet

特點:

  • 基于 TreeMap 實現(xiàn)的有序集合。
  • 最開始的圖上有寫 TreeMap 是繼承 SortedSet 的莫辨,但是我在 JDK1.8 的源碼中看到并沒有沮榜。

TreeSet 無參構造函數

public TreeSet() {
    this(new TreeMap<E,Object>());
}

TreeSet 主要方法

方法名 作用 重載/說明
add(E e) 添加元素 -
addAll(Collection<? extends E> c) 添加整個集合 -
first() 返回集合中首個元素 -
last() 返回集合中最后一個元素 -
pollFirst() 刪除集合首個元素 -
pollLast() 刪除集合最后一個個元素 -
lower(E e) 返回小于該數值的最近元素 參數值可以不存在于 TreeSet 中
higher(E e) 返回大于該數值的最近元素 參數值可以不存在于 TreeSet 中
floor(E e) 返回小于或等于該數值的最近元素 同上
ceiling(E e) 返回大于或等于該數值的最近元素 同上
comparator() 如果TreeSet采用了定制排序,則該方法返回定制排序所使用Comparator振愿;如果TreeSet采用了自然排序萍歉,則返回null枪孩。 -

2.3 接口 Queue extends Collection

Queue

Queue 主要方法:

方法名 作用 說明
boolean add(E var1) 添加元素 添加失敗時會拋出異常
boolean offer(E var1) 添加元素 添加失敗返回 false
E remove() 移除元素 失敗拋出異常
E poll() 移除元素 失敗返回 false
E element() 獲取頭部元素,不移除 隊列為空拋出異常
E peek() 獲取頭部元素攻询,不移除 隊列為空返回 null

2.3.1 接口 Deque extends Queue

Deque

特點

  • 雙端隊列,支持從兩個端點檢索和插入元素拯杠。
方法名 作用 說明
push(E) 添加元素 失敗時拋出異常
addFirst(E) 隊列頭部添加元素 失敗時拋出異常
addLast(E) 隊列尾部添加元素 失敗時拋出異常
offerFirst(E) 隊列頭部添加元素 失敗時返回 false
offerLast(E) 隊列尾部添加元素 失敗時返回 false
getFirst() 獲取隊列頭部元素 隊列為空時拋出異常
getLast() 獲取隊列尾部元素 隊列為空時拋出異常
peekFirst() 獲取隊列頭部元素 隊列為空時返回 null
peekLast() 獲取隊列尾部元素 隊列為空時返回 null
removeFirstOccurrence(Object) 刪除第一次出現(xiàn)的指定元素 不存在時返回false
removeLastOccurrence(Object) 刪除最后一次出現(xiàn)的指定元素 不存在時返回false
pop() 彈出隊列頭部元素 隊列為空時拋出異常
removeFirst() 彈出隊列頭部元素 隊列為空時拋出異常
removeLast() 彈出隊列尾部元素 隊列為空時拋出異常
pollFirst() 彈出隊列頭部元素 隊列為空時返回 null
pollLast() 彈出隊列尾部元素 隊列為空時返回 null

可以看出,Deque 在 Queue 的基礎上添加了針對隊首和隊尾的一些操作最蕾。

Deque 的具體實現(xiàn)

Deque 的直接實現(xiàn)有 ArrayDeque和LinkedList揖膜,它們共同特點是非阻塞拜隧,非線程安全洪添。

ArrayDeque

特點

  • 由 Object[] 來維護數據干奢,也就是數組實現(xiàn)忿峻。
  • 在兩端增刪元素較為高效逛尚。
  • 總體比 LinkedList 要更優(yōu)越到逊。
LinkedList

上面有聊到過 LinkedList 的特點觉壶,所以這里注重和 ArrayDeque 對比铜靶。

特點

  • 鏈表實現(xiàn)旷坦,實現(xiàn)了 Deque 需要有獲取頭和尾的功能,所以是一個雙端鏈表舌胶。
  • 在兩端增刪元素效率較低,因為需要創(chuàng)建和刪除相關結點誊薄。
  • 實現(xiàn)了 List 接口的所有操作切心,較為靈活绽昏。

Deque 的阻塞實現(xiàn)有 BlockingQueue 接口和五個阻塞隊列類全谤,阻塞隊列的特點是不是立即從隊列添加或刪除元素补憾,線程執(zhí)行操作阻塞直到有空間或元素可用余蟹。

阻塞隊列的五個實現(xiàn)類:

  • ArrayBlockingQueue:一個由數組支持的有界隊列威酒。
  • LinkedBlockingQueue:一個由鏈接節(jié)點支持的可選有界隊列葵孤。
  • PriorityBlockingQueue:一個由優(yōu)先級堆支持的無界優(yōu)先級隊列尤仍。
  • DelayQueue:一個由優(yōu)先級堆支持的宰啦、基于時間的調度隊列。
  • SynchronousQueue:一個利用 BlockingQueue 接口的簡單聚集(rendezvous)機制漓柑。

那么它們是怎么實現(xiàn)阻塞的呢辆布,就選第一個實現(xiàn)類 ArrayBlockingQueue 的添加元素方法來看一下:

ArrayBlockingQueue # offer(E e)

public boolean offer(E e) {
    Objects.requireNonNull(e);
    // 1.獲取 ReentrantLock 
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)
            return false;
        else {
            // 2.進行插入數據的操作
            enqueue(e);
            return true;
        }
    } finally {
        // 3.釋放鎖
        lock.unlock();
    }
}

根據注釋 1 2 3 來看,ArrayBlockingQueue 的添加元素的方法使用了 ReentrantLock 重入鎖惭蹂,滿足一定條件后執(zhí)行數據插入操作剿干,最后再釋放鎖。其它主要方法也都有用到重入鎖榜轿,所以說它是一個阻塞隊列谬盐。

其它實現(xiàn)了 BlockingQueue 幾個阻塞隊列基本都是通過加鎖的方式來實現(xiàn)阻塞的皇型,但其中的具體實現(xiàn)和鎖的功能和方式可能不同弃鸦,暫不深究吧唬格。

java并發(fā)編程 之 Queue的一些總結

那么有關 List、Set 和 Queue 的內容先到這里门粪,下篇記錄 Map 相關和總結注服。

Java - 集合框架完全解析
Java 集合框架

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瞭郑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子阁谆,更是在濱河造成了極大的恐慌场绿,老刑警劉巖璧尸,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛀序,居然都是意外死亡徐裸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門瓣颅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倦逐,“玉大人,你說我怎么就攤上這事宫补∶世眩” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵粉怕,是天一觀的道長健民。 經常有香客問我,道長,這世上最難降的妖魔是什么也搓? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘彰亥。我一直安慰自己瘟檩,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布垛叨。 她就那樣靜靜地躺著淤翔,像睡著了一般谐檀。 火紅的嫁衣襯著肌膚如雪音五。 梳的紋絲不亂的頭發(fā)上夯膀,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音,去河邊找鬼药磺。 笑死我碟,一個胖子當著我的面吹牛贩虾,可吹牛的內容都是我干的伊群。 我是一名探鬼主播策精,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼凹联,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了出革?” 一聲冷哼從身側響起攀隔,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤映皆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體盅藻,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡阳惹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雳殊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片橘沥。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夯秃,靈堂內的尸體忽然破棺而出座咆,到底是詐尸還是另有隱情,我是刑警寧澤仓洼,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布介陶,位于F島的核電站,受9級特大地震影響色建,放射性物質發(fā)生泄漏哺呜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一箕戳、第九天 我趴在偏房一處隱蔽的房頂上張望某残。 院中可真熱鬧国撵,春花似錦、人聲如沸驾锰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椭豫。三九已至,卻和暖如春旨指,著一層夾襖步出監(jiān)牢的瞬間赏酥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工谆构, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留裸扶,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓搬素,卻偏偏與公主長得像呵晨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子熬尺,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容