集合知識彙總

實際開發中,常常用到的 ArrayList、LinkedList、HashMap、LinkedHashMap 等集合類,其實涵蓋了不少數據結構和算法,每一個類能夠說都是精華,今天和你們一塊兒來梳理一下!java

1、摘要

在 Java 中,集合大體能夠分爲兩大致系,一個是 Collection,另外一個是 Map,都位於java.util包下。程序員

  • Collection :主要由 List、Set、Queue 接口組成,List 表明有序、重複的集合;其中 Set 表明無序、不可重複的集合;Java 5 又增長了 Queue 體系集合,表明隊列集合。
  • Map:則表明具備映射關係的鍵值對集合。

不少書籍將 List、Set、Queue、Map 等能存放元素的接口體系,也概括爲容器,由於他們能夠存放元素!面試

集合和容器,這二者只是在概念上定義不一樣,好比ArrayList是一個存放數組的對象,真正用起來並不會去關心這個東西究竟是集合仍是容器,把東西用好纔是關鍵!算法

java.util.Collection下的接口和繼承類關係簡易結構圖:
file
https://imgconvert.csdnimg.cn...segmentfault

java.util.Map下的接口和繼承類關係簡易結構圖:
file
https://imgconvert.csdnimg.cn...數組

須要注意的是,網上有些集合架構圖將 Map 畫成繼承自 Collection,實際上 Map 並無繼承 Collection,Map 是單獨的一個集合體系,這點須要糾正一下。
file
https://imgconvert.csdnimg.cn...緩存

在 Java 集合框架中,數據結構和算法能夠說在裏面體現的淋淋盡致,這一點能夠從咱們以前對各個集合類的分析就能夠看的出來,如動態數組、鏈表、紅黑樹、Set、Map、隊列、棧、堆等,基本上只要出去面試,集合框架的話題必定不會少!安全

下面咱們就一塊兒來看看各大數據結構的具體實現!數據結構

2、List

List 集合的特色:存取有序,能夠存儲重複的元素,能夠用下標進行元素的操做。多線程

List 集合中,各個類繼承結構圖:
file
https://imgconvert.csdnimg.cn...

從圖中能夠看出,List 主要實現類有:ArrayList、LinkedList、Vector、Stack。

2.一、ArrayList

ArrayList 是一個動態數組結構,支持隨機存取,在指定的位置插入、刪除效率低(由於要移動數組元素);若是內部數組容量不足則自動擴容,擴容係數爲原來的1.5倍,所以當數組很大時,效率較低。

file
https://imgconvert.csdnimg.cn...

固然,插入刪除也不是效率很是低,在某些場景下,好比尾部插入、刪除,由於不須要移動數組元素,因此效率也很高哦!

ArrayList 是一個非線程安全的類,在多線程環境下使用迭代器遍歷元素時,會報錯,拋ConcurrentModificationException異常!

所以,若是想要在多線程環境下使用 ArrayList,建議直接使用併發包中的CopyOnWriteArrayList!

2.二、LinkedList

LinkedList 是一個雙向鏈表結構,在任意位置插入、刪除都很方便,可是不支持隨機取值,每次都只能從一端開始遍歷,直到找到查詢的對象,而後返回;不過,它不像 ArrayList 那樣須要進行內存拷貝,所以相對來講效率較高,可是由於存在額外的前驅和後繼節點指針,所以佔用的內存比 ArrayList 多一些。
file
https://imgconvert.csdnimg.cn...

LinkedList 底層經過雙向鏈表實現,經過first和last引用分別指向鏈表的第一個和最後一個元素,注意這裏沒有所謂的啞元(某個參數若是在子程序或函數中沒有用到,那就被稱爲啞元),當鏈表爲空的時候first和last都指向null。

2.三、Vector

Vector 也是一個動態數組結構,一個元老級別的類,早在 jdk1.1 就引入進來了,以後在 jdk1.2 裏引進 ArrayList,ArrayList 能夠說是 Vector 的一個迷你版,ArrayList 大部分的方法和 Vector 比較類似!

二者不一樣的是,Vector 中的方法都加了synchronized,保證操做是線程安全的,可是效率低,而 ArrayList 全部的操做都是非線程安全的,執行效率高,但不安全!

對於 Vector,雖然能夠在多線程環境下使用,可是在迭代遍歷元素的時候依然會報錯,拋ConcurrentModificationException異常!

在 JDK 中 Vector 已經屬於過期的類,官方不建議在程序中採用,若是想要在多線程環境下使用 Vector,建議直接使用併發包中的CopyOnWriteArrayList!

2.四、Stack

Stack 是 Vector 的一個子類,本質也是一個動態數組結構,不一樣的是,它的數據結構是先進後出,取名叫棧!

不過,關於 Java 中 Stack 類,有不少的質疑聲,棧更適合用隊列結構來實現,這使得 Stack 在設計上不嚴謹,所以,官方推薦使用 Deque 下的類來是實現棧!

2.五、小結

List 接口各個實現類性能比較,如圖:
file
https://imgconvert.csdnimg.cn...

  • ArrayList(動態數組結構),查詢快(隨意訪問或順序訪問),增刪慢,但在末尾插入刪除,速度與LinkedList相差無幾,可是是非線程安全的!
  • LinkedList(雙向鏈表結構),查詢慢,增刪快,也是非線程安全的!
  • Vector(動態數組結構),由於方法加了同步鎖,相比 ArrayList 執行都慢,基本不在使用,若是須要在多線程下使用,推薦使用併發容器中的CopyOnWriteArrayList來操做,效率高!
  • Stack(棧結構)繼承於Vector,數據是先進後出,基本不在使用,若是要實現棧,推薦使用 Deque 下的 ArrayDeque,效率比 Stack 高!

3、Map

Map是一個雙列集合,其中保存的是鍵值對,鍵要求保持惟一性,值能夠重複。

從上文中,咱們瞭解到 Map 集合結構體系以下:
file
https://imgconvert.csdnimg.cn...

從圖中能夠看出,Map 的實現類有 HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties 等等。

3.一、HashMap

關於 HashMap,相信你們都不陌生,是一個使用很是頻繁的容器類,繼承自 AbstractMap,它容許鍵值都放入 null 元素,可是 key 不可重複。

由於使用的是哈希表存儲元素,因此輸入的數據與輸出的數據,順序基本不一致,另外,HashMap 最多隻容許一條記錄的 key 爲 null。

在 jdk1.7中,HashMap 主要是由 數組+ 單向鏈表 組成,當發生 hash 衝突的時候,就將衝突的元素放入鏈表中。
file
https://imgconvert.csdnimg.cn...

從 jdk1.8開始,HashMap 主要是由 數組+單向鏈表+紅黑樹 實現,相比 jdk1.7 而言,多了一個紅黑樹實現。

當鏈表長度超過 8 的時候,就將鏈表變成紅黑樹,如圖所示。
file

HashMap 的實現原理,算是面試必問的一個話題,詳細的實現過程,有興趣的朋友能夠閱讀小編以前寫的文章《深刻分析HashMap》

HashMap 雖然很強大,可是它是非線程安全的,也就是說,若是在多線程環境下使用,可能由於程序自動擴容操做將單向鏈表轉變成了循環鏈表,在查詢遍歷元素的時候,形成程序死循環!此時 CPU 直接會飆到 100%!

若是咱們想在多線程環境下使用 HashMap,其中一個推薦的解決辦法就是使用 java 併發包下的 ConcurrentHashMap 類!

在 JDK1.7 中,ConcurrentHashMap 類採用了分段鎖的思想,將 HashMap 進行切割,把 HashMap 中的哈希數組切分紅小數組(Segment),每一個小數組有 n 個 HashEntry 組成,其中 Segment 繼承自ReentrantLock(可重入鎖),從而實現併發控制!

file
https://imgconvert.csdnimg.cn...

從 jdk1.8 開始,ConcurrentHashMap 類取消了 Segment 分段鎖,採用 CAS + synchronized來保證併發安全,數據結構跟 jdk1.8 中 HashMap 結構保持一致,都是 數組 + 鏈表(當鏈表長度大於8時,鏈表結構轉爲紅黑樹)結構。
在這裏插入圖片描述
https://imgconvert.csdnimg.cn...

jdk1.8 中的 ConcurrentHashMap 中 synchronized 只鎖定當前鏈表或紅黑樹的首節點,只要節點 hash 不衝突,就不會產生併發,相比 JDK1.7 的 ConcurrentHashMap 效率又提高了 N 倍!

詳細的實現原理,能夠參閱小編以前寫的文章《深刻分析ConcurrentHashMap》

3.二、LinkedHashMap

LinkedHashMap 繼承自 HashMap,能夠認爲是 HashMap + LinkedList,它既使用 HashMap 操做數據結構,又使用 LinkedList 維護插入元素的前後順序,內部採用雙向鏈表(doubly-linked list)的形式將全部元素( entry )鏈接起來。

從名字上,就能夠看出 LinkedHashMap 是一個 LinkedList 和 HashMap 的混合體,同時知足 HashMap 和 LinkedList 的某些特性,可將 LinkedHashMap 看做採用 Linkedlist 加強的HashMap。
file
https://imgconvert.csdnimg.cn...

雙向鏈表頭部插入的數據爲鏈表的入口,迭代器遍歷方向是從鏈表的頭部開始到鏈表尾部結束,結構圖以下:
file
https://imgconvert.csdnimg.cn...

這種結構除了能夠保持迭代的順序,還有一個好處:迭代 LinkedHashMap 時不須要像 HashMap 那樣遍歷整個 table,而只須要直接遍歷 header 指向的雙向鏈表便可,也就是說 LinkedHashMap 的迭代時間就只跟 entry 的個數相關,而跟 table 的大小無關。

LinkedHashMap 繼承了 HashMap,全部大部分功能特性與 HashMap 基本相同,容許放入 key 爲null的元素,也容許插入 value 爲null的元素。

兩者惟一的區別是 LinkedHashMap 在 HashMap 的基礎上,採用雙向鏈表(doubly-linked list)的形式將全部 entry 鏈接起來,這樣是爲保證元素的迭代順序跟插入順序相同。

3.三、TreeMap

TreeMap實現了 SortedMap 接口,也就是說會按照 key 的大小順序對 Map 中的元素進行排序,key 大小的評判能夠經過其自己的天然順序(natural ordering),也能夠經過構造時傳入的比較器(Comparator)。

TreeMap 底層經過紅黑樹(Red-Black tree)實現,因此要了解 TreeMap 就必須對紅黑樹有必定的瞭解。

以下爲紅黑樹圖:
file
https://imgconvert.csdnimg.cn...

紅黑樹是基於平衡二叉樹實現的,基於平衡二叉樹的實現還有 AVL 算法,也被稱爲 AVL 樹,AVL 算法要求任何節點的兩棵子樹的高度差不大於1,爲了保持樹的高度差不大於1,主要有2中調整方式:左旋轉、右旋轉。

繞某元素右旋轉,圖解以下:
file
https://imgconvert.csdnimg.cn...

繞某元素右旋轉,圖解以下:
file
https://imgconvert.csdnimg.cn...

雖然 AVL 算法保證了樹高度平衡,查詢效率極高,可是也有缺陷,在刪除某個節點時,須要屢次左旋轉或右旋轉調整,紅黑樹的出現就是爲了解決儘量少的調整,提升平衡二叉樹的總體性能!

那麼對於一棵有效的紅黑樹,主要有如下規則:

一、每一個節點要麼是紅色,要麼是黑色,但根節點永遠是黑色的;

二、每一個紅色節點的兩個子節點必定都是黑色;

三、紅色節點不能連續(也便是,紅色節點的孩子和父親都不能是紅色);

四、從任一節點到其子樹中每一個葉子節點的路徑都包含相同數量的黑色節點;

五、全部的葉節點都是是黑色的(注意這裏說葉子節點實際上是上圖中的 NIL 節點);

紅黑樹爲了保證樹的基本平衡,調整也有相似 AVL 算法那樣的左旋轉、右旋轉操做;同時,紅黑樹由於紅色節點不能連續,所以還增長一個顏色調整操做:節點顏色轉換。
file
https://imgconvert.csdnimg.cn...

AVL雖然能夠保證平衡二叉樹高度平衡,可是在刪除某個節點的時候,最多須要 n 次調整,也就是左旋轉、右旋轉;而紅黑樹,雖然也是基於平衡二叉樹實現,可是並不像 AVL 那樣高度平衡,而是基本平衡,在刪除某個節點的時候,至多 3 次調整,效率比 AVL 高!

紅黑樹,在插入的時候,與 AVL 同樣,結點最多隻須要2次旋轉;在查詢方面,由於不是高度平衡,紅黑樹在查詢效率方面稍遜於 AVL,可是比二叉查找樹強不少!

就總體來看,紅黑樹的性能要好於 AVL,只是實現稍微複雜了一些,有興趣的朋友能夠參閱小編以前寫的文章《3分鐘看完關於樹的故事》

3.四、IdentityHashMap

IdentityHashMap 從名字上看,感受表示惟一的 HashMap,而後並非,別被它的名稱所欺騙哦。

IdentityHashMap 的數據結構很簡單,底層實際就是一個 Object 數組,在存儲上並無使用鏈表來存儲,而是將 K 和 V 都存放在 Object 數組上。
file
https://imgconvert.csdnimg.cn...

當添加元素的時候,會根據 Key 計算獲得散列位置,若是發現該位置上已經有改元素,直接進行新值替換;若是沒有,直接進行存放。當元素個數達到必定閾值時,Object 數組會自動進行擴容處理。

IdentityHashMap 的實現也不一樣於 HashMap,雖然也是數組,不過 IdentityHashMap 中沒有用到鏈表,解決衝突的方式是計算下一個有效索引,而且將數據 key 和 value 緊挨着存入 map 中,即table[i]=key、table[i+1]=value;

IdentityHashMap 容許key、value都爲null,當key爲null的時候,默認會初始化一個Object對象做爲key;

3.五、WeakHashMap

WeakHashMap 是 Map 體系中一個很特殊的成員,它的特殊之處在於 WeakHashMap 裏的元素可能會被 GC 自動刪除,即便程序員沒有顯示調用 remove() 或者 clear() 方法。

換言之,當向 WeakHashMap 中添加元素的時候,再次遍歷獲取元素,可能發現它已經不見了,咱們來看看下面這個例子。

public static void main(String[] args) {
        Map weakHashMap = new WeakHashMap();
        //向weakHashMap中添加3個元素
        weakHashMap.put("key-1", "value-1");
        weakHashMap.put("key-2", "value-2");
        weakHashMap.put("key-3", "value-3");
        //輸出添加的元素
        System.out.println("數組長度:"+weakHashMap.size() + ",輸出結果:" + weakHashMap);
        //主動觸發一次GC
        System.gc();
        //再輸出添加的元素
        System.out.println("數組長度:"+weakHashMap.size() + ",輸出結果:" + weakHashMap);
}

輸出結果:
數組長度:3,輸出結果:{key-2=value-2, key-1=value-1, key-0=value-0}
數組長度:3,輸出結果:{}
當主動調用 GC 回收器的時候,再次查詢 WeakHashMap 裏面的數據的時候,數據已經被 GC 收集了,內容爲空。

這是由於 WeakHashMap 的 key 使用了弱引用類型,在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。

不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。

WeakHashMap 跟普通的 HashMap 不一樣,在存儲數據時,key 被設置爲弱引用類型,而弱引用類型在 java 中,可能隨時被 jvm 的 gc 回收,因此再次經過獲取對象時,可能獲得空值,而 value 是在訪問數組內容的時候,進行清除。

可能不少人以爲這樣作很奇葩,其實否則,WeekHashMap 的這個特色特別適用於須要緩存的場景。

在緩存場景下,因爲系統內存是有限的,不能緩存全部對象,可使用 WeekHashMap 進行緩存對象,即便緩存丟失,也能夠經過從新計算獲得,不會形成系統錯誤。

比較典型的例子,Tomcat 中的 ConcurrentCache 類就使用了 WeekHashMap 來實現數據緩存。

3.六、Hashtable

Hashtable 一個元老級的集合類,早在 JDK 1.0 就誕生了,而 HashMap 誕生於 JDK 1.2。

在實現上,HashMap 能夠看做是 Hashtable 的一個迷你版,雖然兩者的底層數據結構都是 數組 + 鏈表 結構,具備查詢、插入、刪除快的特色,可是兩者又有不少的不一樣。

  • 雖然 HashMap 和 Hashtable 都實現了 Map 接口,但 Hashtable 繼承於 Dictionary 類,而 HashMap 是繼承於 AbstractMap;
  • HashMap 能夠容許存在一個爲 null 的 key 和任意個爲 null 的 value,可是 HashTable 中的 key 和 value 都不容許爲 null;
  • Hashtable 的方法是同步的,由於在方法上加了 synchronized 同步鎖,而 HashMap 是非線程安全的;

雖然 Hashtable 是 HashMap 線程安全的一個版本,可是官方已經不推薦使用 HashTable了,由於歷史遺留緣由,並無移除此類,若是須要在線程安全的環境下使用 HashMap,那麼推薦使用 ConcurrentHashMap。在上文中已經有所提到!

3.七、Properties

在 Java 中,其實還有一個很是重要的類 Properties,它繼承自 Hashtable,主要用於讀取配置文件。

Properties 類是 java 工具包中很是重要的一個類,好比在實際開發中,有些變量,咱們能夠直接硬寫入到自定義的 java 枚舉類中。

可是有些變量,在測試環境、預生產環境、生產環境,變量所須要取的值都不同,這個時候,咱們能夠經過使用 properties 文件來加載程序須要的配置信息,以達到一行代碼,多處環境均可以運行的效果!

好比,最多見的 JDBC 數據源配置文件,properties文件以.properties做爲後綴,文件內容以鍵=值格式書寫,左邊是變量名稱,右邊是變量值,用#作註釋,新建一個jdbc.properties文件,內容以下:
file

Properties 繼承自 Hashtable,大部分方法都複用於 Hashtable,與 Hashtable 不一樣的是, Properties 中的 key 和 value 都是字符串。

實際開發中,Properties 主要用於讀取配置文件,尤爲是在不一樣的環境下,變量值須要不同的狀況,能夠經過讀取配置文件來避免將變量值寫死在 java 的枚舉類中,以達到一行代碼,多處運行的目的!

4、Set

Set集合的特色:元素不重複,存取無序,無下標。

Set 集合,在元素存儲方面,注重獨立無二的特性,若是某個元素在集合中已經存在,不會存儲重複的元素,同時,集合存儲的是元素,不像 Map 集合那樣存儲的是鍵值對。

Set 集合中,各個類繼承結構圖:
file
https://imgconvert.csdnimg.cn...

從圖中能夠看出,Set 集合主要實現類有: HashSet、LinkedHashSet 、TreeSet 、EnumSet( RegularEnumSet、JumboEnumSet )等等

4.一、HashSet

HashSet 是一個輸入輸出無序的集合,底層基於 HashMap 來實現,HashSet 利用 HashMap 中的 key 元素來存放元素,這一點咱們能夠從源碼上看出來,源碼定義以下:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable{
    // HashMap 變量
    private transient HashMap<E,Object> map;
    /**HashSet 初始化*/
    public HashSet() {
        //默認實例化一個 HashMap
        map = new HashMap<>();
    }
}

由於 HashMap 容許 key 爲空爲null,因此 HashSet 也容許添加爲null的元素。

4.二、LinkedHashSet

LinkedHashSet 是一個輸入輸出有序的集合,繼承自 HashSet,可是底層基於 LinkedHashMap 來實現。

LinkedHashSet 的源碼,類定義以下:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    public LinkedHashSet() {
        //調用 HashSet 的方法
        super(16, .75f, true);
    }
}

查詢源碼,super調用的方法,源碼以下:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        //初始化一個 LinkedHashMap
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

LinkedHashSet 與 HashSet 相比,LinkedHashSet 保證了元素輸入輸出有序!

4.三、TreeSet

TreeSet 是一個排序的集合,實現了NavigableSet、SortedSet、Set接口,底層基於 TreeMap 來實現。

TreeSet 利用 TreeMap 中的 key 元素來存放元素,這一點咱們也能夠從源碼上看出來,閱讀源碼,類定義以下:

public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
    //TreeSet 使用NavigableMap接口做爲變量
    private transient NavigableMap<E,Object> m;
    /**對象初始化*/
    public TreeSet() {
        //默認實例化一個 TreeMap 對象
        this(new TreeMap<E,Object>());
    }
    //對象初始化調用的方法
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
}

new TreeSet<>()對象實例化的時候,表達的意思,能夠簡化爲以下:

NavigableMap<E,Object> m = new TreeMap<E,Object>();

由於TreeMap實現了NavigableMap接口,因此沒啥問題。

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable{
    ......
}

TreeSet 是一個排序的 Set 集合,元素不可重複,底層基於 TreeMap 的 key 來實現,元素不能夠爲空,默認按照天然排序來存放元素,也可使用 Comparable和 Comparator 接口來比較大小,實現自定義排序。

4.四、EnumSet

EnumSet 是一個與枚舉類型搭配使用的專用 Set 集合,在 jdk1.5 中加入。

與 HashSet、LinkedHashSet 、TreeSet 不一樣的是,EnumSet 元素必須是Enum的類型,而且全部元素都必須來自同一個枚舉類型。

新建一個EnumEntity的枚舉類型,定義2個參數。

public enum EnumEntity {
    WOMAN,MAN;
}

建立一個 EnumSet,並將枚舉類型的元素所有添加進去!

//建立一個 EnumSet,將EnumEntity 元素內容添加到EnumSet中
EnumSet<EnumEntity> allSet = EnumSet.allOf(EnumEntity.class);
System.out.println(allSet);

輸出結果:
[WOMAN, MAN]

在 Java 中,EnumSet 是一個虛類,有2個實現類 RegularEnumSet、JumboEnumSet,不能顯式的實例化改類,EnumSet 會動態決定使用哪個實現類,當元素個數小於等於64的時候,使用 RegularEnumSet;大於 64的時候,使用JumboEnumSet類。

EnumSet 其內部使用位向量實現,擁有極高的時間和空間性能,若是元素是枚舉類型,推薦使用 EnumSet。

4.五、小結

file
https://imgconvert.csdnimg.cn...

5、Queue

在 jdk1.5 中,新增了 Queue 接口,表明一種隊列集合的實現。

Queue 接口是由大名鼎鼎的 Doug Lea 建立,中文名爲道格·利,翻開 JDK1.8 源代碼,能夠將 Queue 接口旗下的實現類抽象成以下結構圖:

file
https://imgconvert.csdnimg.cn...

從上圖能夠看出,Queue 接口主要實現類有:ArrayDeque、LinkedList、PriorityQueue。

5.一、ArrayDeque

ArrayQueue 是一個基於數組實現的雙端隊列,能夠想象,在隊列中存在兩個指針,一個指向頭部,一個指向尾部,所以它具備FIFO隊列及LIFO棧的方法特性。

其中隊列(FIFO)表示先進先出,好比水管,先進去的水先出來;棧(LIFO)表示先進後出,好比,手槍彈夾,最後進去的子彈,最早出來。

以下爲 ArrayDeque 數據結構圖,head 表示頭部指針,tail表示尾部指針:
file
https://imgconvert.csdnimg.cn...

在上文中,咱們也說到 Stack 也能夠做爲棧使用,可是 ArrayDeque 的效率要高於 Stack 類,而且功能也比 Stack 類豐富的多,當須要使用棧時,Java 已不推薦使用 Stack,而是推薦使用更高效的 ArrayDeque。

5.二、LinkedList

LinkedList 是一個基於鏈表實現的雙端隊列,在上文中咱們也說到 LinkedList 實現自 List,做爲一個雙向鏈表時,增長或刪除元素的效率較高,若是查找的話須要遍歷整個鏈表,效率較低!
file
https://imgconvert.csdnimg.cn...

於此同時,LinkedList 也實現了 Deque 接口,既能夠做隊列使用也能夠做爲棧使用。

從上圖中能夠得出,ArrayDeque 和 LinkedList 都是 Deque 接口的實現類,都具有既能夠做爲隊列,又能夠做爲棧來使用的特性,二者主要區別在於底層數據結構的不一樣。

ArrayDeque 底層數據結構是以循環數組爲基礎,而 LinkedList 底層數據結構是以循環鏈表爲基礎。

理論上,鏈表在添加、刪除方面性能高於數組結構,在查詢方面數組結構性能高於鏈表結構,可是對於數組結構,若是不進行數組移動,在添加方面效率也很高。

下面,分別以10萬條數據爲基礎,經過添加、刪除,來測試二者做爲隊列、棧使用時所消耗的時間,測試結果以下圖:
file
https://imgconvert.csdnimg.cn...

從數據上能夠看出,在 10 萬條數據下,二者性能都差很少,當達到 100 萬條、1000 萬條數據的時候,二者的差異就比較明顯了,ArrayDeque 不管是做爲隊列仍是做爲棧使用,性能均高於 LinkedList 。

爲何 ArrayDeque 性能,在大數據量的時候,明顯高於 LinkedList?

我的分析,LinkedList 底層是以循環鏈表來實現的,每個節點都有一個前驅、後繼的變量,也就是說,每一個節點上都存放有它上一個節點的指針和它下一個節點的指針,同時還包括它本身的元素,每次插入或刪除節點時須要修改前驅、後繼節點變量,在同等的數據量狀況下,鏈表的內存開銷要明顯大於數組,同時由於 ArrayDeque 底層是數組結構,自然在查詢方面佔優點,在插入、刪除方面,只須要移動一下頭部或者尾部變量,時間複雜度是 O(1)。

因此,在大數據量的時候,LinkedList 的內存開銷明顯大於 ArrayDeque,在插入、刪除方面,都要頻發修改節點的前驅、後繼變量;而 ArrayDeque 在插入、刪除方面依然保存高性能。

若是對於小數據量,ArrayDeque 和 LinkedList 在效率方面相差不大,可是對於大數據量,推薦使用 ArrayDeque。

5.三、PriorityQueue

PriorityQueue 並無直接實現 Queue接口,而是經過繼承 AbstractQueue 類來實現 Queue 接口一些方法,在 Java 定義中,PriorityQueue 是一個基於優先級的無界優先隊列。

通俗的說,添加到 PriorityQueue 隊列裏面的元素都通過了排序處理,默認按照天然順序,也能夠經過 Comparator 接口進行自定義排序。

優先隊列的做用是保證每次取出的元素都是隊列中權值最小的。

PriorityQueue 排序實現與上文中說到的 TreeMap 相似。
file
https://imgconvert.csdnimg.cn...

在 Java 中 PriorityQueue 是一個使用數組結構來存儲元素的優先隊列,雖然它也實現了Queue接口,可是元素存取並非先進先出,而是經過一個二叉小頂堆實現的,默認底層使用天然排序規則給插入的元素進行排序,也可使用自定義比較器來實現排序,每次取出的元素都是隊列中權值最小的。

同時須要注意的是,PriorityQueue 不能插入null,不然報空指針異常!

6、總結

以上主要是對 java 集合往期文章內容作了一個簡單的總結,有興趣的能夠參閱各篇詳細內容,若是有理解不當之處,歡迎指正!

轉自:https://blog.csdn.net/javagee...

相關文章
相關標籤/搜索