集合類--最詳細的面試寶典--看這篇就夠用了(java 1.8)

  看了一個星期源碼,搜索上百篇博文,終於總結出了集合類的全部基礎知識點,學集合,看這篇就夠用了!!!java

  篇幅有點長, 若是你能所有理解,java最重要的集合就不怕了,秒過面試!!!(本篇素材來自網絡,若有冒犯請見諒,)程序員

 

在看集合類以前, 咱們要先明白一下概念:面試

1.數據結構

  (1):線性表

 

 

    [1]:順序存儲結構(也叫順序表)

      一個線性表是n個具備相同特性的數據元素的有限序列。數據元素是一個抽象的符號,其具體含義在不一樣的狀況下通常不一樣。算法

    [2]:鏈表

      鏈表裏面節點的地址不是連續的,是經過指針連起來的。編程

  (2):哈希表

解釋一:數組

 

 

哈希表hashtable(key,value) 就是把Key經過一個固定的算法函數既所謂的哈希函數轉換成一個整型數字,而後就將該數字對數組長度進行取餘,取餘結果就看成數組的下標,將value存儲在以該數字爲下標的數組空間裏。緩存

解釋二:安全

數組的特色是:尋址容易,插入和刪除困難;網絡

而鏈表的特色是:尋址困難,插入和刪除容易。數據結構

那麼咱們能不能綜合二者的特性,作出一種尋址容易,插入刪除也容易的數據結構?答案是確定的,這就是咱們要提起的哈希表,哈希表有多種不一樣的實現方法,我接下來解釋的是最經常使用的一種方法——拉鍊法,咱們能夠理解爲「鏈表的數組」,如圖:

 

 

左邊很明顯是個數組,數組的每一個成員包括一個指針,指向一個鏈表的頭,固然這個鏈表可能爲空,也可能元素不少。咱們根據元素的一些特徵把元素分配到不一樣的鏈表中去,也是根據這些特徵,找到正確的鏈表,再從鏈表中找出這個元素。

 

Hash 表的查詢速度很是的快,幾乎是O(1)的時間複雜度。

hash就是找到一種數據內容和數據存放地址之間的映射關係。

散列法:元素特徵轉變爲數組下標的方法。

我想你們都在想一個很嚴重的問題:「若是兩個字符串在哈希表中對應的位置相同怎麼辦?」,畢竟一個數組容量是有限的,這種可能性很大。解決該問題的方法不少,我首先想到的就是用「鏈表」。我遇到的不少算法均可以轉化成鏈表來解決,只要在哈希表的每一個入口掛一個鏈表,保存全部對應的字符串就OK了。

散列表的查找步驟 

當存儲記錄時,經過散列函數計算出記錄的散列地址

當查找記錄時,咱們經過一樣的是散列函數計算記錄的散列地址,並按此散列地址訪問該記錄

 

優缺點

優勢:不論哈希表中有多少數據,查找、插入、刪除(有時包括刪除)只須要接近常量的時間即0(1)的時間級。實際上,這隻須要幾條機器指令。

哈希表運算得很是快,在計算機程序中,若是須要在一秒種內查找上千條記錄一般使用哈希表(例如拼寫檢查器)哈希表的速度明顯比樹快,樹的操做一般須要O(N)的時間級。哈希表不只速度快,編程實現也相對容易。

若是不須要有序遍歷數據,而且能夠提早預測數據量的大小。那麼哈希表在速度和易用性方面是無與倫比的。

缺點:它是基於數組的,數組建立後難於擴展,某些哈希表被基本填滿時,性能降低得很是嚴重,因此程序員必需要清楚表中將要存儲多少數據(或者準備好按期地把數據轉移到更大的哈希表中,這是個費時的過程)。

 

哈希表的原理:

   1,對對象元素中的關鍵字(對象中的特有數據),進行哈希算法的運算,並得出一個具體的算法值,這個值 稱爲哈希值

  2,哈希值就是這個元素的位置。

  3,若是哈希值出現衝突,再次判斷這個關鍵字對應的對象是否相同。若是對象相同,就不存儲,由於元素重複。若是對象不一樣,就存儲,在原來對象的哈希值基礎 +1順延。

  4,存儲哈希值的結構,咱們稱爲哈希表。

  5,既然哈希表是根據哈希值存儲的,爲了提升效率,最好保證對象的關鍵字是惟一的。

  這樣能夠儘可能少的判斷關鍵字對應的對象是否相同,提升了哈希表的操做效率。

擴展:

相同的字符串若是存進去,哈希值相同而且equals方法爲true,不會存入相同的

只要哈希值相同或者equals方法爲true都成立纔不會存入,只要其中一條不知足,都會儲存

 

哈希表存儲過程:

1.調用對象的哈希值(經過一個函數f()獲得哈希值):存儲位置 = f(關鍵字)

2.集合在容器內搜索有沒有重複的哈希值,若是沒有,存入新元素,記錄哈希值

3.再次存儲,重複上邊的過程

4.若是有重複的哈希值,調用後來者的equals方法,參數爲前來者,結果獲得true,集合判斷爲重複元素,不存入

 

哈希衝突

然而萬事無完美,若是兩個不一樣的元素,經過哈希函數得出的實際存儲地址相同怎麼辦?也就是說,當咱們對某個元素進行哈希運算,獲得一個存儲地址,而後要進行插入的時候,發現已經被其餘元素佔用了,其實這就是所謂的哈希衝突,也叫哈希碰撞。前面咱們提到過,哈希函數的設計相當重要,好的哈希函數會盡量地保證 計算簡單散列地址分佈均勻,可是,咱們須要清楚的是,數組是一塊連續的固定長度的內存空間,再好的哈希函數也不能保證獲得的存儲地址絕對不發生衝突。那麼哈希衝突如何解決呢?

哈希衝突的解決方案有多種:

開放定址法(發生衝突,繼續尋找下一塊未被佔用的存儲地址)

再散列函數法

鏈地址法,而HashMap便是採用了鏈地址法,也就是數組+鏈表的方式

 

關於hashcode和equals的一些問題,在面試中會問道:

1.兩個對象哈希值相同,那麼equals方法必定返回true嗎?

不必定:取決於如何重寫equals,若是重寫固定了它返回false,結果就必定是false

2.equals方法返回true,那麼哈希值必定相同嗎?

必定:若是類中定義一個靜態變量(static int a = 1),而後重寫hashcode返回a+1,那麼每個對象的哈希值都不同,不過java中規定:對象相等,必須具備相同的哈希碼值,因此這裏是必定的

 

  (3)數組

採用一段連續的存儲單元來存儲數據。對於指定下標的查找,時間複雜度爲O(1);經過給定值進行查找,須要遍歷數組,逐一比對給定關鍵字和數組元素,時間複雜度爲O(n),固然,對於有序數組,則可採用二分查找,插值查找,斐波那契查找等方式,可將查找複雜度提升爲O(logn);對於通常的插入刪除操做,涉及到數組元素的移動,其平均複雜度也爲O(n) 

  (4)區別

1.數組

優勢:(1)隨機訪問效率高(根據下標查詢),(2)搜索效率較高(可以使用折半方法)。

缺點:(1)內存連續且固定,存儲效率低。(2)插入和刪除效率低(可能會進行數組拷貝或擴容)。

2.鏈表

優勢:(1)不要求連續內存,內存利用率高,(2)插入和刪除效率高(只須要改變指針指向)。

缺點:(1)不支持隨機訪問,(2)搜索效率低(須要遍歷)。

3.Hash表

優勢:(1)搜索效率高,(2)插入和刪除效率較高,

缺點:(1)內存利用率低(基於數組),(2)存在散列衝突。

2.集合類種重要概念詞解釋

不弄清楚這些詞的概念, 總感受集合類學的很模糊!!!

 

  (1).泛型

java中很重要的概念, 集合裏面應用不少.

集合的元素,能夠是任意類型對象的引用,若是把某個對象放入集合,則會忽略它的類型,就會把它當作Object類型處理.

泛型則是規定了某個集合只能夠存放特定類型的對象的引用,會在編譯期間進行類型檢查,能夠直接指定類型來獲取集合元素

在泛型集合中有可以存入泛型類型的對象實例還能夠存入泛型的子類型的對象實例

注意:

1 泛型集合中的限定類型,不能使用基本數據類型

2 能夠經過使用包裝類限定容許存放基本數據類型

 

泛型的好處

1 提升了安全性(將運行期的錯誤轉換到編譯期)

2 省去強轉的麻煩

  (2).哈希值

  1 就是一個十進制的整數,有操做系統隨機給出

  2 能夠使用Object類中的方法hashCode獲取哈希值

  3 Object中源碼: int hashCode()返回該對象的哈希碼值;

  源碼:

    public native int hashCode();

    native:指調用了本地操做系統的方法實現

  (3).平衡二叉樹(稱AVL樹)

其特色是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹。也就是說該二叉樹的任何一個子節點,其左右子樹的高度都相近。

注意:

關鍵點是左子樹和右子樹的深度的絕對值不超過1

那什麼是左子樹深度和右子樹深度呢?

 

如上圖中:

若是插入6元素, 則8的左子樹深度就爲2, 右子樹深度就爲0,絕對值就爲2, 就不是一個平很二叉樹

 

[1].二叉排序樹

1若左子樹不空,則左子樹上全部結點的值均小於它的根結點的值;

2若右子樹不空,則右子樹上全部結點的值均大於它的根結點的值;

3左、右子樹也分別爲二叉排序樹

解釋一:

如今有a[10] = {3, 2, 1, 4, 5, 6, 7, 10, 9, 8}須要構建二叉排序樹。在沒有學習平衡二叉樹以前,根據二叉排序樹的特性,一般會將它構建成以下左圖。雖然徹底符合二叉排序樹的定義,可是對這樣高度達到8的二叉樹來講,查找是很是不利的。所以,更加指望構建出以下右圖的樣子,高度爲4的二叉排序樹,這樣才能夠提供高效的查找效率。

 

平衡二叉樹是一種二叉排序樹,是一種高度平衡的二叉樹,其中每一個結點的左子樹和右子樹的高度至多等於1.意味着:要麼是一棵空樹,要麼左右都是平衡二叉樹,且左子樹和右子樹深度之絕對值不超過1. 將二叉樹上結點的左子樹深度減去右子樹深度的值稱爲平衡因子BF,那麼平衡二叉樹上的全部結點的平衡因子只多是-一、0和1。只要二叉樹上有一個結點的平衡因子的絕對值大於1,則該二叉樹就是不平衡的。

平衡二叉樹的前提是它是一棵二叉排序樹。

[2].旋轉

假設一顆 AVL 樹的某個節點爲 r,有四種操做會使 r 的左右子樹高度差大於 1,從而破壞了原有 AVL 樹的平衡性。使用旋轉達到平衡性

1.對 r 的左兒子的左子樹進行一次插入(左旋轉 LL)

 

 2.對 r 的左兒子的右子樹進行一次插入(LR)

 

3.對 r 的右兒子的左子樹進行一次插入(RL)

 

4.對 r 的右兒子的右子樹進行一次插入(RR)

 

 

   (4).紅黑樹

紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹

(1) 檢索效率O(log n)

(2) 紅黑樹的五點規定:

1.每一個結點要麼是紅的要麼是黑的

2.根結點是黑的

3.每一個葉結點(葉結點即指樹尾端NIL指針或NULL結點)都是黑的

4.若是一個結點是紅的,那麼它的兩個兒子都是黑的(反之不必定)

5.對於任意結點而言,其到葉結點樹尾端NIL指針的每條路徑都包含相同數目的黑結點

 

它的每一個結點都額外有一個顏色的屬性,顏色只有兩種:紅色和黑色。

示例:(這塊難度比較大, 建議自行百度,查閱相關文檔)

 

紅黑樹插入操做

若是是第一次插入,因爲原樹爲空,因此只會違反紅黑樹的規則2,因此只要把根節點塗黑便可;

若是插入節點的父節點是黑色的,那不會違背紅-黑樹的規則,什麼也不須要作;

可是遇到以下三種狀況時,咱們就要開始變色和旋轉了:

        1. 插入節點的父節點和其叔叔節點(祖父節點的另外一個子節點)均爲紅色的;

        2. 插入節點的父節點是紅色,叔叔節點是黑色,且插入節點是其父節點的右子節點;

        3. 插入節點的父節點是紅色,叔叔節點是黑色,且插入節點是其父節點的左子節點。

        下面咱們先挨個分析這三種狀況都須要如何操做:

        對於狀況1插入節點的父節點和其叔叔節點(祖父節點的另外一個子節點)均爲紅色的。此時,確定存在祖父節點,可是不知道父節點是其左子節點仍是右子節點,可是因爲對稱性,咱們只要討論出一邊的狀況,另外一種狀況天然也與之對應。

這裏考慮父節點是祖父節點的左子節點的狀況(即插入一個4節點,插入的節點通常爲紅色,否則可能違反規則5.),以下左圖所示:

        

        對於這種狀況,咱們要作的操做有:將當前節點(4)的父節點(5)和叔叔節點(8)塗黑,將祖父節點(7)塗紅,變成上右圖所示的狀況。再將當前節點指向其祖父節點,再次重新的當前節點開始算法。這樣上右圖就變成了狀況2了。

        對於狀況2插入節點的父節點是紅色,叔叔節點是黑色,且插入節點是其父節點的右子節點。咱們要作的操做有:將當前節點(7)的父節點(2)做爲新的節點,以新的當前節點爲支點作左旋操做。完成後如左下圖所示,這樣左下圖就變成狀況3了。

 

        對於狀況3插入節點的父節點是紅色,叔叔節點是黑色,且插入節點是其父節點的左子節點。咱們要作的操做有:將當前節點的父節點(7)塗黑,將祖父節點(11)塗紅,在祖父節點爲支點作右旋操做。最後把根節點塗黑,整個紅-黑樹從新恢復了平衡,如右上圖所示。至此,插入操做完成!

咱們能夠看出,若是是從狀況1開始發生的,必然會走完狀況2和3,也就是說這是一整個流程,固然咯,實際中可能不必定會從狀況1發生,若是從狀況2開始發生,那再走個狀況3便可完成調整,若是直接只要調整狀況3,那麼前兩種狀況均不須要調整了。故變色和旋轉之間的前後關係能夠表示爲:變色->左旋->右旋。

 

紅黑樹刪除操做

 咱們如今約定:後繼節點的子節點稱爲「當前節點」.

刪除節點有三種狀況分析:

        a. 葉子節點;(直接刪除便可)

 

       b. 僅有左或右子樹的節點;(上移子樹便可)

 

       c. 左右子樹都有的節點。( 用刪除節點的直接前驅或者直接後繼來替換當前節點,調整直接前驅或者直接後繼的位置)

 

刪除操做後,若是當前節點是黑色的根節點,那麼不用任何操做,由於並無破壞樹的平衡性,即沒有違背紅-黑樹的規則,這很好理解。若是當前節點是紅色的,說明剛剛移走的後繼節點是黑色的,那麼無論後繼節點的父節點是啥顏色,咱們只要將當前節點塗黑就能夠了,紅-黑樹的平衡性就能夠恢復。可是若是遇到如下四種狀況,咱們就須要經過變色或旋轉來恢復紅-黑樹的平衡了。

        1. 當前節點是黑色的,且兄弟節點是紅色的(那麼父節點和兄弟節點的子節點確定是黑色的);

        2. 當前節點是黑色的,且兄弟節點是黑色的,且兄弟節點的兩個子節點均爲黑色的;

       3. 當前節點是黑色的,且兄弟節點是黑色的,且兄弟節點的左子節點是紅色,右子節點時黑色的;

       4. 當前節點是黑色的,且兄弟節點是黑色的,且兄弟節點的右子節點是紅色,左子節點任意顏色。

        以上四種狀況中,咱們能夠看出2,3,4實際上是「當前節點是黑色的,且兄弟節點是黑色的」的三種子集,等會在程序中能夠體現出來。如今咱們假設當前節點是左子節點(固然也多是右子節點,跟左子節點相反便可,咱們討論一邊就能夠了),分別解決上面四種狀況:

        對於狀況1:當前節點是黑色的,且兄弟節點是紅色的(那麼父節點和兄弟節點的子節點確定是黑色的)。如左下圖所示:A節點表示當前節點。針對這種狀況,咱們要作的操做有:將父節點(B)塗紅,將兄弟節點(D)塗黑,而後將當前節點(A)的父節點(B)做爲支點左旋,而後當前節點的兄弟節點就變成黑色的狀況了(天然就轉換成狀況2,3,4的公有特徵了),如右下圖所示:  

        對於狀況2:當前節點是黑色的,且兄弟節點是黑色的,且兄弟節點的兩個子節點均爲黑色的。如左下圖所示,A表示當前節點。針對這種狀況,咱們要作的操做有:將兄弟節點(D)塗紅,將當前節點指向其父節點(B),將其父節點指向當前節點的祖父節點,繼續新的算法(具體見下面的程序),不須要旋轉。這樣變成了右下圖所示的狀況:

  

        對於狀況3:當前節點是黑色的,且兄弟節點是黑色的,且兄弟節點的左子節點是紅色,右子節點時黑色的。如左下圖所示,A是當前節點。針對這種狀況,咱們要作的操做有:把當前節點的兄弟節點(D)塗紅,把兄弟節點的左子節點(C)塗黑,而後以兄弟節點做爲支點作右旋操做。而後兄弟節點就變成黑色的,且兄弟節點的右子節點變成紅色的狀況(狀況4)了。如右下圖:

  

        對於狀況4:當前節點是黑色的,且兄弟節點是黑色的,且兄弟節點的右子節點是紅色,左子節點任意顏色。如左下圖所示:A爲當前節點,針對這種狀況,咱們要作的操做有:把兄弟節點(D)塗成父節點的顏色,再把父節點(B)塗黑,把兄弟節點的右子節點(E)塗黑,而後以當前節點的父節點爲支點作左旋操做。至此,刪除修復算法就結束了,最後將根節點塗黑便可。

 

        咱們能夠看出,若是是從狀況1開始發生的,可能狀況2,3,4中的一種:若是是狀況2,就不可能再出現3和4;若是是狀況3,必然會致使狀況4的出現;若是2和3都不是,那必然是4。固然咯,實際中可能不必定會從狀況1發生,這要看具體狀況了。不懂的歡迎加羣討論: QQ羣:123300273 (相親相愛的一家人) 

  (5).迭代器

  [1].迭代器模式

把訪問邏輯從不一樣類型的集合類中抽取出來,從而避免向外部暴露集合的內部結構。在java中它是一個對象,其目的是遍歷並選中其中的每一個元素,而使用者(客戶端)無需知道里面的具體細節。

 

[2].Iterator

Collection集合元素的通用獲取方式:在取出元素以前先判斷集合中有沒有元素。若是有,就把這個元素取出來,繼續再判斷,若是還有就再取出來,一直把集合中的全部元素所有取出來,這種取出元素的方式專業術語稱爲迭代。

java.util.Iterator:在Java中Iterator爲一個接口,它只提供了迭代的基本規則。在JDK中它是這樣定義的:對Collection進行迭代的迭代器。迭代器取代了Java Collection Framework中的Enumeration。

Collection中有一個抽象方法iterator方法,全部的Collection子類都實現了這個方法;返回一個Iterator對象

定義:

package java.util;

public interface Iterator<E> {

    boolean hasNext();//判斷是否存在下一個對象元素

    E next();//獲取下一個元素

    void remove();//移除元素

}

在使用Iterator的時候禁止對所遍歷的容器進行改變其大小結構的操做。例如: 在使用Iterator進行迭代時,若是對集合進行了add、remove操做就會出現ConcurrentModificationException異常。

在進行集合元素取出的時候,若是集合中沒有元素了,還繼續使用next()方法的話,將發生NoSuchElementException沒有集合元素的錯誤

修改併發異常:在迭代集合中元素的過程當中,集合的長度發生改變(進行了元素增長或者元素刪除的操做), 加強for的底層原理也是迭代器,因此也須要避免這種操做;

解決以上異常的方法:使用ListIterator

 

任何集合都有迭代器。

任何集合類,都必須能以某種方式存取元素,不然這個集合容器就沒有任何意義。

迭代器,也是一種模式(也叫迭代器模式)。迭代器要足夠的「輕量」——建立迭代器的代價小。

 

[3].Iterable(1.5)

Java中還提供了一個Iterable接口,Iterable接口實現後的功能是‘返回’一個迭代器,咱們經常使用的實現了該接口的子接口有:Collection<E>、List<E>、Set<E>等。該接口的iterator()方法返回一個標準的Iterator實現。實現Iterable接口容許對象成爲Foreach語句的目標。就能夠經過foreach語句來遍歷你的底層序列。

Iterable接口包含一個能產生Iterator對象的方法,而且Iterable被foreach用來在序列中移動。所以若是建立了實現Iterable接口的類,均可以將它用於foreach中。

定義:

  Package java.lang; import java.util.Iterator; public interface Iterable<T> {    Iterator<T> iterator(); }

Iterable是Java 1.5的新特性, 主要是爲了支持forEach語法, 使用容器的時候, 若是不關心容器的類型, 那麼就須要使用迭代器來編寫代碼. 使代碼可以重用.

使用方法很簡單:

  List<String> strs = Arrays.asList("a", "b", "c"); for (String str: strs) {    out.println(str); }

好處:代碼減小,方便遍歷

       弊端:沒有索引,不能操做容器裏的元素

加強for循環底層也是使用了迭代器獲取的,只不過獲取迭代器由jvm完成,不須要咱們獲取迭代器而已,因此在使用加強for循環變量元素的過程當中不許使用集合對象對集合的元素個數進行修改;

 

[4].forEach()(1.8)

使用接收lambda表達式的forEach方法進行快速遍歷.

  List<String> strs = Arrays.asList("a", "b", "c"); // 使用Java 1.8的lambda表達式 strs.forEach(out::println);

 

[5].Spliterator迭代器

    Spliterator是1.8新增的迭代器,屬於並行迭代器,能夠將迭代任務分割交由多個線程來進行。

Spliterator能夠理解爲Iterator的Split版本(但用途要豐富不少)。使用Iterator的時候,咱們能夠順序地遍歷容器中的元素,使用Spliterator的時候,咱們能夠將元素分割成多份,分別交於不於的線程去遍歷,以提升效率。使用 Spliterator 每次能夠處理某個元素集合中的一個元素 — 不是從 Spliterator 中獲取元素,而是使用 tryAdvance() 或 forEachRemaining() 方法對元素應用操做。但Spliterator 還能夠用於估計其中保存的元素數量,並且還能夠像細胞分裂同樣變爲一分爲二。這些新增長的能力讓流並行處理代碼能夠很方便地將工做分佈到多個可用線程上完成

 

[6].ListIterator

ListIterator是一個更強大的Iterator子類型,能用於各類List類訪問,前面說過Iterator支持單向取數據,ListIterator能夠雙向移動,因此能指出迭代器當前位置的前一個和後一個索引,能夠用set方法替換它訪問過的最後一個元素。咱們能夠經過調用listIterator方法產生一個指向List開始處的ListIterator,而且還能夠用太重載方法listIterator(n)來建立一個指定列表索引爲n的元素的ListIterator。

ListIterator能夠往前遍歷,添加元素,設置元素

 

Iterator和ListIterator的區別:

二者都有next()和hasNext(),能夠實現向後遍歷,可是ListIterator有previous()和hasPrevious()方法,便可以實現向前遍歷

ListIterator能夠定位當前位置,nextIndex()和previous()能夠實現

ListIterator有add()方法,能夠向list集合中添加數據

均可以實現刪除操做,可是ListIterator能夠實現對對象的修改,set()能夠實現,Iterator僅能遍歷,不能修改

 

[7]Fail-Fast

類中的iterator()方法和listIterator()方法返回的iterators迭代器是fail-fast的:當某一個線程A經過iterator去遍歷某集合的過程當中,若該集合的內容被其餘線程所改變了;那麼線程A訪問集合時,就會拋出ConcurrentModificationException異常,產生fail-fast事件。

迭代器與枚舉有兩點不一樣:

  1. 迭代器在迭代期間能夠從集合中移除元素。

  2. 方法名獲得了改進,Enumeration的方法名稱都比較長。

迭代器的好處:屏蔽了集合之間的不一樣,能夠使用相同的方式取出

 

3.集合類概念

(1).集合類的做用

集合類也叫作容器類,和數組同樣,用於存儲數據,但數組類型單一,而且長度固定,限制性很大,而集合類能夠動態增長長度。

集合存儲的元素都是對象(引用地址),因此集合能夠存儲不一樣的數據類型,但若是是須要比較元素來排序的集合,則須要類型一致。

集合中提供了統一的增刪改查方法,使用方便。

支持泛型,避免數據不一致和轉換異常,還對經常使用的數據結構進行了封裝。

全部的集合類的都在java.util包下。

(2)集合框架體系的組成

集合框架體系是由Collection、Map(映射關係)和Iterator(迭代器)組成,各部分的做用以下所示。

[1]Collection體系中有三種集合:Set、List、Queue

 Set(集): 元素是無序的且不可重複。

 List(列表):元素是有序的且可重複。

 Queue(隊列):封裝了數據結構中的隊列。

[2]Map體系

 Map用於保存具備映射關係的數據,即key-value(鍵值對)。Map集合的key是惟一的,不可重複,而value能夠重複。因此一個value能夠對應多個key。

 Map體系除了經常使用類以外,還有Properties(屬性類)也屬於Map體系。

[3]Iterator(迭代器)

請查看上面!

(3)Collection的由來

  因爲數組中存放對象,對對象操做起來不方便。java中有一類容器,專門用來存儲對象。

 集合能夠存儲多個元素,但咱們對多個元素也有不一樣的需求

 多個元素,不能有相同的

 多個元素,可以按照某個規則排序

 針對不一樣的需求:java就提供了不少集合類,多個集合類的數據結構不一樣。可是,結構不重要,重要  的是可以存儲東西,可以判斷,獲取.

把集合共性的內容不斷往上提取,最終造成集合的繼承體系---->Collection

而且全部的Collection實現類都重寫了toString()方法.

 

(4)集合和數組

集合與數組的區別:

    1.數組的長度固定的,而集合長度時可變的

2.數組只能儲存同一類型的元素,並且能存基本數據類型和引用數據類型。集合能夠存儲不一樣類型的元素,只能存儲引用數據類型

集合類和數組不同,數組元素既能夠是基本類型的值,也能夠是對象(實際上保存的是對象的引用變量);而集合只能保存對象。

數組和集合的主要區別包括如下幾個方面:

一:數組聲明瞭它容納的元素的類型,而集合不聲明。這是因爲集合以object形式來存儲它們的元素。

二:一個數組實例具備固定的大小,不能伸縮。集合則可根據須要動態改變大小。

三:數組是一種可讀/可寫數據結構沒有辦法建立一個只讀數組。然而能夠使用集合提供的ReadOnly方   只讀方式來使用集合。該方法將返回一個集合的只讀版本。

集合的做用:

若是一個類的內部有不少相同類型的屬性,而且他們的做用與意義是同樣的,好比說學生能選課學生類就有不少課程類型的屬性,或者工廠類有不少機器類型的屬性,咱們用一個相似於容器的集合去盛裝他們,這樣在類的內部就變的井井有理---------這就是:

      •  在類的內部,對數據進行組織的做用。
      •  簡單而快速的搜索查找其中的某一條元素
      •  有的集合接口,提供了一系列排列有序的元素,而且能夠在序列中間快速的插入或者刪除有關元素。
      •  有的集合接口在其內部提供了映射關係的結構,能夠經過關鍵字(key)去快速查找對應的惟一對象,而這個關鍵能夠是任意類型的。

 

(5)泛型與集合的區別

泛型聽起來很高深的一個詞,但實際上它的做用很簡單,就是提升java程序的性能。 

好比在計算機中常常用到一些數據結構,如隊列,鏈表等,而其中的元素之前通常這麼定義:object a=new object(); 

這樣就帶來一個嚴重的問題,用object來表示元素沒有邏輯問題,但每次拆箱、封箱就佔用了大量的計算機資源,致使程序性能低下,而這部份內容偏偏通常都是程序的核心部分,若是使用object,那麼程序的表現就比較糟糕。

而使用泛型則很好的解決這個問題,本質就是在編譯階段就告訴編譯器,數據結構中元素的種類,既然編譯器知道了元素的種類,天然就避免了拆箱、封箱的操做,從而顯著提升java程序的性能。 

好比List<string>就直接使用string對象做爲List的元素,而避免使用object對象帶來的封箱、拆箱操做,從而提升程序性能。

 

4.集合接口與類

(1)數組和集合通常就用到下面接口和集合

Array  數組

Arrays  數組工具

Collection 最基本的集合接口

Collections  集合工具類

List  接口

ArrayList 一種能夠動態增加和縮減的索引序列

LinkedList 一種能夠在任何位置進行高效地插入和刪除操做的有序序列

Vector

Set

HashSet 一種沒有重複元素的無序集合

TreeSet 一種有序集

LinkHashSet 一種能夠記住元素插入次序的集合

map

HashMap 一種存儲key:value關聯的映射

HashTable

TreeMap 一種key有序的映射

LinkedHashMap 一種能夠記住插入次序的映射

Deque

Stack

ArrayDeque  一種用循環數組實現的雙端隊列

Queue

PriorityQueue 一種能夠高效刪除最小元素的集合

 

(2)Array

數組:是以一段連續內存保存數據的;隨機訪問是最快的,但不支持插入,刪除,迭代等操做。

Array能夠包含基本類型和對象類型

Array大小是固定的

指定數組引用爲 null,則此類中的方法都會拋出 NullPointerException。

所建立的對象都放在堆中。

夠對自身進行枚舉(由於都實現了IEnumerable接口)。

具備索引(index),便可以經過index來直接獲取和修改任意項。

Array類型的變量在聲明的同時必須進行實例化(至少得初始化數組的大小),而ArrayList能夠只是先聲明。

 Array只能存儲同構的對象,而ArrayList能夠存儲異構的對象。

在CLR託管對中的存放方式

Array是始終是連續存放的,而ArrayList的存放不必定連續。

Array不可以隨意添加和刪除其中的項,而ArrayList能夠在任意位置插入和刪除項。

採用數組存在的一些缺陷:

   1.數組長度固定不變,不能很好地適應元素數量動態變化的狀況。

   2.可經過數組名.length獲取數組的長度,卻沒法直接獲取數組中真實存儲的個數。

   3.在進行頻繁插入、刪除操做時一樣效率低下。

 

(3)Arrays

數組的工具類,裏面都是操做數組的工具.

經常使用方法:

一、數組的排序:Arrays.sort(a);//實現了對數組從小到大的排序//注:此類中只有升序排序,而無降序排序。

二、數組元素的定位查找:Arrays.binarySearch(a,8);//二分查找法

三、數組的打印:Arrays.toString(a);//String 前的a和括號中的a均表示數組名稱

四、 查看數組中是否有特定的值:Arrays.asList(a).contains(1);

 

(4)Collection

Collection是最基本的集合接口,一個Collection表明一組Object,即Collection的元素(Elements)。一些 Collection容許相同的元素而另外一些不行。一些能排序而另外一些不行。Java SDK不提供直接繼承自Collection的類, Java SDK提供的類都是繼承自Collection的「子接口」如List和Set。

全部實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用於建立一個空的Collection,有一個Collection參數的構造函數用於建立一個新的 Collection,這個新的Collection與傳入的Collection有相同的元素。後一個構造函數容許用戶複製一個Collection。

如何遍歷Collection中的每個元素?不論Collection的實際類型如何,它都支持一個iterator()的方法,該方法返回一個迭代子,使用該迭代子便可逐一訪問Collection中每個元素。典型的用法以下:

    Iterator it = collection.iterator(); // 得到一個迭代子

    while(it.hasNext()) {

      Object obj = it.next(); // 獲得下一個元素

由Collection接口派生的兩個接口是List和Set。

Collection返回的是Iterator迭代器接口,而List中又有它本身對應的實現-->ListIterator接口 Collection。標識所含元素的序列,這裏面又包含多種集合類,好比List,Set,Queue;它們都有各自的特色,好比List是按順序插入元素,Set是不重複元素集合,Queue則是典型的FIFO結構

Collection接口描述:

  Collection接口經常使用的子接口有List 接口和Set接口

  List接口中經常使用的子類有:ArrayList類(數組列表)和LinkedList(鏈表)

  Set接口中經常使用的子類有:HashSet (哈希表)和LinkedHashSet(基於鏈表的哈希表)

Collection 層次結構 中的根接口。Collection 表示一組對象,這些對象也稱爲 collection 的元素。一些 collection 容許有重複的元素,而另外一些則不容許。一些 collection 是有序的,而另外一些則是無序的。JDK 不提供此接口的任何直接 實現:它提供更具體的子接口(如 Set和 List)實現。此接口一般用來傳遞 collection,並在須要最大廣泛性的地方操做這些 collection。(面向接口的編程思想)

 

(5)Collections

[1]排序操做

Collections提供如下方法對List進行排序操做

void reverse(List list):反轉

void shuffle(List list),隨機排序

void sort(List list),按天然排序的升序排序

void sort(List list, Comparator c);定製排序,由Comparator控制排序邏輯

void swap(List list, int i , int j),交換兩個索引位置的元素

void rotate(List list, int distance),旋轉。當distance爲正數時,將list後distance個元素總體移到前面。當distance爲負數時,將 list的前distance個元素總體移到後面。

 

[2]查找,替換操做

int binarySearch(List list, Object key), 對List進行二分查找,返回索引,注意List必須是有序的

int max(Collection coll),根據元素的天然順序,返回最大的元素。 類比int min(Collection coll)

int max(Collection coll, Comparator c),根據定製排序,返回最大元素,排序規則由Comparatator類控制。類比int min(Collection coll, Comparator c)

void fill(List list, Object obj),用元素obj填充list中全部元素

int frequency(Collection c, Object o),統計元素出現次數

int indexOfSubList(List list, List target), 統計targe在list中第一次出現的索引,找不到則返回-1,類比int lastIndexOfSubList(List source, list target).

boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替換舊元素。

 

[3]同步控制

Collections中幾乎對每一個集合都定義了同步控制方法, 這些方法,來將集合包裝成線程安全的集合

 SynchronizedList(List);

 SynchronizedSet(Set;

  SynchronizedMap(Map);

 

SynchronizedMap和ConcurrentHashMap 區別

Collections.synchronizedMap()和Hashtable同樣,實現上在調用map全部方法時,都對整個map進行同步,而ConcurrentHashMap的實現卻更加精細,它對map中的全部桶加了鎖。因此,只要要有一個線程訪問map,其餘線程就沒法進入map,而若是一個線程在訪問ConcurrentHashMap某個桶時,其餘線程,仍然能夠對map執行某些操做。這樣,ConcurrentHashMap在性能以及安全性方面,明顯比Collections.synchronizedMap()更加有優點。同時,同步操做精確控制到桶,因此,即便在遍歷map時,其餘線程試圖對map進行數據修改,也不會拋出ConcurrentModificationException。

ConcurrentHashMap從類的命名就能看出,它必然是個HashMap。而Collections.synchronizedMap()能夠接收任意Map實例,實現Map的同步

 

線程安全,而且鎖分離。ConcurrentHashMap內部使用段(Segment)來表示這些不一樣的部分,每一個段其實就是一個小的hashtable,它們有本身的鎖。只要多個修改操做發生在不一樣的段上,它們就能夠併發進行。 

 

(6)List

List:有序(元素存入集合的順序和取出的順序一致),元素都有索引。元素能夠重複。

List自己是Collection接口的子接口,具有了Collection的全部方法。

List的特有方法都有索引,這是該集合最大的特色。

List集合支持對元素的增、刪、改、查。

List中存儲的元素實現類排序,並且能夠重複的存儲相關元素。

次序是List最重要的特色:它保證維護元素特定的順序。

 

List是有序的Collection,使用此接口可以精確的控制每一個元素插入的位置。用戶可以使用索引(元素在List中的位置,相似於數組下標)來訪問List中的元素,這相似於Java的數組。

和下面要提到的Set不一樣,List容許有相同的元素。

 

除了具備Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,返回一個 ListIterator接口,和標準的Iterator接口相比,ListIterator多了一些add()之類的方法,容許添加,刪除,設定元素,還能向前或向後遍歷。

 

優勢:操做讀取操做效率高,基於數組實現的,能夠爲null值,能夠容許重複元素,有序,異步。

缺點:因爲它是由動態數組實現的,不適合頻繁的對元素的插入和刪除操做,由於每次插入和刪除都須要移動數組中的元素。

 

(7)ArrayList

ArrayList 是基於數組實現,內存中分配連續的空間,須要維護容量大小。隨機訪問.

ArrayList就是動態數組,也是一個對象。

ArrayList不自定義位置添加元素和LinkedList性能沒啥區別,ArrayList默認元素追加到數組後面,而LinkedList只須要移動指針,因此二者性能相差無幾。

若是ArrayList自定義位置插入元素,越靠前,須要重寫排序的元素越多,性能消耗越大,LinkedList不管插入任何位置都同樣,只須要建立一個新的表項節點和移動一下指針,性能消耗很低。

ArrayList是基於數組,因此查看任意位置元素只須要獲取當前位置的下標的數組就能夠,效率很高,然而LinkedList獲取元素須要從最前面或者最後面遍歷到當前位置元素獲取,若是集合中元素不少,就會效率很低,性能消耗大。

頻繁遍歷查看元素,使用 ArrayList 集合,ArrayList 查詢快,增刪慢

ArrayList線程不安全的

一、ArrayList是用數組實現的,該對象存放在堆內存中,這個數組的內存是連續的,不存在相鄰元素之間還隔着其餘內存。底層是一個可動態擴容的數組

二、索引ArrayList時,速度比原生數組慢是由於你要用get方法,這是一個函數調用,而數組直接用[ ]訪問,至關於直接操做內存地址,速度固然比函數調用快。

三、新建ArrayList的時候,JVM爲其分配一個默認或指定大小的連續內存區域(封裝爲數組)。

四、每次增長元素會檢查容量,不足則建立新的連續內存區域(大小等於初始大小+步長),也用數組形式封裝,並將原來的內存區域數據複製到新的內存區域,而後再用ArrayList中引用原來封裝的數組對象的引用變量引用到新的數組對象:

    elementData = Arrays.copyOf(elementData, newCapacity);

  ArrayList裏面的removeIf方法就接受一個Predicate參數,採用以下Lambda表達式就能把,全部null元素刪除:

    list.removeIf(e -> e == null);

ArrayList:每次添加元素以前會檢查是否須要擴容,是按照原數組的1.5倍延長。構造一個初始容量爲 10 的空列表。

使用for適合循環ArrayLIst以及數組,當大批量的循環LinkedList時程序將會卡死,for適合循環數組結構,經過下標去遍歷。

get訪問List內部任意元素時,ArrayList的性能要比LinkedList性能好。LinkedList中的get方法是要按照順序從列表的一端開始檢查,直到另外一端。

在ArrayList的 中間插入或刪除一個元素意味着這個列表中剩餘的元素都會被移動;

ArrayList的空 間浪費主要體如今在list列表的結尾預留必定的容量空間

ArrayList只能包含對象類型。

ArrayList的大小是動態變化的。 

對於基本類型數據,集合使用自動裝箱來減小編碼工做量

夠對自身進行枚舉(由於實現了IEnumerable接口)。

具備索引(index),便可以經過index來直接獲取和修改任意項。

ArrayList容許存放(不止一個)null元素

容許存放重複數據,存儲時按照元素的添加順序存儲

 

ArrayList能夠存聽任何不一樣類型的數據(由於它裏面存放的都是被裝箱了的Object型對象,實際上ArrayList內部就是使用"object[] _items;"這樣一個私有字段來封裝對象的)

ArrayList不是一個線程安全的集合,若是集合的增刪操做須要保證線程的安全性,能夠考慮使用CopyOWriteArrayList或者使用collections.synchronizedList(Lise l)函數返回一個線程安全的ArrayList類。

 實現了RandomAccess接口,底層又是數組,get讀取元素性能很好

 順序添加很方便

 刪除和插入須要複製數組 性能不好(能夠使用LinkindList)

 

爲何ArrayList的elementData是用transient修飾的?

transient修飾的屬性意味着不會被序列化,也就是說在序列化ArrayList的時候,不序列化elementData。

爲何要這麼作呢?

 elementData不老是滿的,每次都序列化,會浪費時間和空間

 重寫了writeObject 保證序列化的時候雖然不序列化所有 可是有的元素都序列化

因此說不是不序列化 而是不所有序列化。

elementData屬性採用了transient來修飾,不使用Java默認的序列化機制來實例化,本身實現了序列化writeObject()和反序列化readObject()的方法。

 

每次對下標的操做都會進行安全性檢查,若是出現數組越界就當即拋出異常。

若是提早知道數組元素較多,能夠在添加元素前經過調用ensureCapacity()方法提早增長容量以減少後期容量自動增加的開銷。

當須要插入大量元素時,在插入前能夠調用ensureCapacity方法來增長ArrayList的容量以提升插入效率。

 

ArrayList基於數組方式實現,容量限制不大於Integer.MAX_VALUE的小大,每次擴容1.5倍。有序,能夠爲null,容許重複,非線程安全。

增長和刪除會修改modCount,在迭代的時候須要保持單線程的惟一操做,若是期間進行了插入或者刪除操做,就會被迭代器檢查獲知,從而出現運行時異常。

 

通常建議在單線程中使用ArrayList。

 當在index處放置一個元素的時候,會將數組index處右邊的元素所有右移

 當在index處刪除一個元素的時候,會將數組index處右邊的元素所有左移

 

ArrayList底層是數組結構,由於數組有維護索引,因此查詢效率高;而作插入、刪除操做時,由於要判斷擴容(複製一份新數組)且數組中的元素可能要大規模的後移或前移一個索引位置,因此效率差。

Arrays.asList()方法返回的List集合是一個固定長度的List集合,不是ArrayList實例,也不是Vector的實例

ArrayList也採用了快速失敗(Fail-Fast機制)的機制,經過記錄modCount參數來實現。在面對併發的修改時,迭代器很快就會徹底失敗,而不是冒着在未來某個不肯定時間發生任意不肯定行爲的風險。具體介紹請參考HashMap的實現原理中的Fail-Fast機制。

 

(8)linkedList

LinkedList 是基於循環雙向鏈表數據結構,不須要維護容量大小。順序訪問。

頻繁插入刪除元素 使用 LinkedList 集合

LinkedList 線程不安全的

LinkedList在隨機訪問方面相對比較慢,可是它的特性集較ArrayList 更大。

LinkedList提供了大量的首尾操做

LinkedList:底層的數據結構是鏈表,線程不一樣步,增刪元素的速度很是快。

LinkedList:底層基於鏈表實現,鏈表內存是散亂的,每個元素存儲自己內存地址的同時還存儲下一個元素的地址。鏈表增刪快,查找慢 

LinkedList由雙鏈表實現,增刪因爲不須要移動底層數組數據,其底層是鏈表實現的,只須要修改鏈表節點指針,對元素的插入和刪除效率較高。

LinkedList缺點是遍歷效率較低。HashMap和雙鏈表也有關係。

LinkedList是一個繼承於AbstractSequentialList的雙向鏈表,它能夠被當作堆棧、隊列或雙端隊列進行操做

LinkedList可被用做堆棧(stack),隊列(queue)或雙向隊列(deque)。

 

使用foreach適合循環LinkedList,使用雙鏈表結構實現的應當使用foreach循環。

LinkedList實現了List接口,容許null元素。

LinkedList沒有同步方法。若是多個線程同時訪問一個List,則必須本身實現訪問同步。一種解決方法是在建立List時構造一個同步的List:

       List list = Collections.synchronizedList(new LinkedList(…));

在LinkedList的中間插入或刪除一個元素的開銷是固定的。 

LinkedList不支持高效的隨機元素訪問。 

LinkedList的空間花費則體如今它的每個元素都須要消耗至關的空間 

LinkedList是List和Deque接口的雙向鏈表的實現。實現了全部可選列表操做,並容許包括null值

 

 Fail-Fast機制:LinkedList也採用了快速失敗的機制,經過記錄modCount參數來實現。在面對併發的修改時,迭代器很快就會徹底失敗,而不是冒着在未來某個不肯定時間發生任意不肯定行爲的風險。

 

LinkedList由於底層爲鏈表結構,查詢時須要從頭節點(或尾節點)開始遍歷因此查詢效率差;但同時也由於是鏈表結構,作插入、刪除操做時只要斷開當前刪除節點前驅、後驅引用,並將原來的前、後節點的引用連接起來,因此效率高。

千萬不要使用普通for循環遍歷LinkedList,這麼作會讓你崩潰!能夠選擇使用foreach或迭代器來進行遍歷操做

 LinedList適合用迭代遍歷;

基於鏈表結構的集合 LinkedList。LinkedList 屬於 java.util 包下面,也實現Iterable接口,說明能夠使用迭代器遍歷;LinkedList 還實現 Deque<E>,Queue<E> 操做。Deque 和 Queue 是 LinkedList 的父接口,那麼 LinkedList 也能夠當作一種 Deque 或者 Queue;Queue表示一種隊列,也是一種數據結構,它的特色是先進先出,所以在隊列這個接口裏面提供了一些操做隊列的方法,同時LinkedList也具備這些方法;Deque(Double ended queues雙端隊列),支持在兩端插入或者移除元素; 那也應該具備操做雙端隊列的一些方法;LinkedList 是他們的子類,說明都具備他們二者的方法;LinkedList也能夠充當隊列,雙端隊列,堆棧多個角色。

 

(9)vector

Vector:底層的數據結構就是數組,線程同步的,Vector不管查詢和增刪都巨慢。

Vector:是按照原數組的2倍延長。

Vector是基於線程安全的,效率低 元素有放入順序,元素可重複 

Vector能夠由咱們本身來設置增加的大小,ArrayList沒有提供相關的方法。

Vector相對ArrayList查詢慢(線程安全的)

Vector相對LinkedList增刪慢(數組結構)

之前還能見到Vector和Stack,但Vector太過古老,被ArrayList取代,因此這裏不講;而Stack已經被ArrayDeque取代。

 

對於想在迭代器迭代過程當中針對集合進行增刪改的,能夠經過返回ListIterator來操做。

Vector:底層結構是數組,線程是安全的,添加刪除慢,查找快,(同ArrayList)

 

ArrayList 和Vector是採用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增長和插入元素,都容許直接序號索引元素,可是插入數據要涉及到數組元素移動等內存操做,因此索引數據快,插入數據慢,Vector因爲使用了synchronized方法(線程安全)因此性能上比ArrayList要差,LinkedList使用雙向鏈表實現存儲,按序號索引數據須要進行向前或向後遍歷,可是插入數據時只須要記錄本項的先後項便可,因此插入數度較快。

 

Vector 是矢量隊列,它是JDK1.0版本添加的類。繼承於AbstractList,實現了List, RandomAccess, Cloneable這些接口。

Vector 實現了RandmoAccess接口,即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,爲List提供快速訪問功能的。在Vector中,咱們便可以經過元素的序號快速獲取元素對象;這就是快速隨機訪問。

Vector 實現了Cloneable接口,即實現clone()函數。它能被克隆。

 

由Vector建立的Iterator,當一個Iterator被建立並且正在被使用,另外一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,所以必須捕獲該異常。

 

(10)Set

無序(存入和取出順序有可能不一致),不能夠存儲重複元素。必須保證元素惟一性。

元素無放入順序,元素不可重複(注意:元素雖然無放入順序,可是元素在set中的位置是有該元素的HashCode決定的,其位置實際上是固定的)

 

Set具備與Collection徹底同樣的接口,所以沒有任何額外的功能,只是行爲不一樣。這是繼承與多態思想的典型應用:表現不一樣的行爲。

Set不保存重複的元素(至於如何判斷元素相同則較爲負責)

存入Set的每一個元素都必須是惟一的,由於Set不保存重複元素,加入Set的元素必須定義equals()方法以確保對象的惟一性。

Set 是基於對象的值來肯定歸屬性的。

 

Set自己有去重功能是由於String內部重寫了hashCode()和equals()方法,在add裏實現了去重, Set集合是不容許重複元素的,可是集合是不知道咱們對象的重複的判斷依據的,默認狀況下判斷依據是判斷二者是否爲同一元素(euqals方法,依據是元素==元素),若是要依據咱們本身的判斷來判斷元素是否重複,須要重寫元素的equals方法(元素比較相等時調用)hashCode()的返回值是元素的哈希碼,若是兩個元素的哈希碼相同,那麼須要進行equals判斷。【因此能夠自定義返回值做爲哈希碼】 equals()返回true表明兩元素相同,返回false表明不一樣。

set集合沒有索引,只能用迭代器或加強for循環遍歷

 

set的底層是map集合

Set最多有一個null元素

必須當心操做可變對象(Mutable Object)。若是一個Set中的可變元素改變了自身狀態致使Object.equals(Object)=true將致使一些問題。

Set具備與Collection徹底同樣的接口,沒有額外的任何功能。因此把Set就是Collection,只是行爲不一樣(這就是多態);Set是基於對象的值來判斷歸屬的,因爲查詢速度很是快速,HashSet使用了散列,HashSet維護的順序與TreeSet或LinkedHashSet都不一樣,由於它們的數據結構都不一樣,元素存儲方式天然也不一樣。TreeSet的數據結構是「紅-黑樹」,HashSet是散列函數,LinkedHashSet也用了散列函數;若是想要對結果進行排序,那麼選擇TreeSet代替HashSet是個不錯的選擇

 

(11)Hashset

HashSet : 爲快速查找設計的Set。存入HashSet的對象必須定義hashCode()。

Hashset實現set接口,由哈希表(其實是一個HashMap實例)支持。它不保證set的迭代順序,別是它不保證該順序恆久不變。此類容許使用Null元素

對於HashSet而言,它是基於HashMap實現的,HashSet底層使用HashMap來保存全部元素,所以HashSet的實現比較簡單,相關HashSet的操做,基本上都說調用HashMap的相關方法來實現的

對於HashSet中保存的對象,請注意正確重寫其equals和hashCode方法,以保證放入的對象的惟一性。

HashSet: 哈希表結構的集合 利用哈希表結果構成的集合查找速度會很快。

HashSet : 底層數據結構是哈希表,線程 是不一樣步的 。 無序,高效;HashSet 集合保證元素惟一性 :經過元素的 hashCode 方法,和 equals 方法完成的。當元素的 hashCode 值相同時,才繼續判斷元素的 equals 是否爲 true。若是爲 true,那麼視爲相同元素,不存。若是爲 false,那麼存儲。若是 hashCode 值不一樣,那麼不判斷 equals,從而提升對象比較的速度。

 

HashSet類直接實現了Set接口, 其底層實際上是包裝了一個HashMap去實現的。HashSet採用HashCode算法來存取集合中的元素,所以具備比較好的讀取和查找性能。

元素值能夠爲NULL,但只能放入一個null

          HashSet集合保證元素惟一性:經過元素的hashCode方法,和equals方法完成的。

 

當元素的hashCode值相同時,才繼續判斷元素的equals是否爲true。

若是hashCode值不一樣,那麼不判斷equals,從而提升對象比較的速度。

對於HashSet集合,判斷元素是否存在,或者刪除元素,底層依據的是hashCode方法和equals方法。  

 

特色:存儲取出都比較快

一、不能保證元素的排列順序,順序可能與添加順序不一樣,順序也有可能發生變化。

二、HashSet不是同步的,必須經過代碼來保證其同步。

三、集合元素能夠是null.

原理:簡單說就是鏈表數組結合體

 

對象的哈希值:普通的一個整數,能夠理解爲身份證號,是hashset存儲的依據

HashSet按Hash算法來存儲集合中的元素。在存取和查找上有很好的性能。

當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來獲得該對象的hashCode值,而後根據該hashCode值決定該hashCode值決定該對象在HashSet中存儲的位置。

若是有兩個元素經過equals()方法比較返回true,但它們的hashCode()方法返回值不相等,hashSet將會把它們存儲在不一樣位置,依然能夠添加成功。若是兩個對象的hashCode()方法返回的hashCode值相同,當它們的equals()方法返回false時,會在hashCode所在位置採用鏈式結構保存多個對象。這樣會下降hashSet的查詢性能。

 

在使用HashSet中重寫hashCode()方法的基本原則

一、在程序運行過過程當中,同一個對象屢次調用hashCode()方法應該返回相同的值。

二、當兩個對象的equals()方法比較返回true時,這個兩個對象的hashCode()方法返回相同的值。

三、對象中用做equals()方法比較標準的實例變量,都應該用於計算hashCode值。 

把對象內的每一個意義的實例變量(即每一個參與equals()方法比較標準的實例變量)計算出一個int類型的hashCode值。用第1步計算出來的多個hashCode值組合計算出一個hashCode值返回 

 return f1.hashCode()+(int)f2; 

爲了不直接相加產生的偶然相等(兩個對象的f一、f2實例變量並不相等,但他們的hashCode的和剛好相等),能夠經過爲各個實例變量的hashCode值乘以一個質數後再相加

 return f1.hashCode()*19+f2.hashCode()*37; 

 

若是向HashSet中添加一個可變的對象後,後面的程序修改了該可變對想的實例變量,則可能致使它與集合中的其餘元素的相同(即兩個對象的equals()方法比較返回true,兩個對象的hashCode值也相等),這就有可能致使HashSet中包含兩個相同的對象。

 

(12)Linkedhashset

 LinkedHashSet : 具備HashSet的查詢速度,且內部使用鏈表維護元素的順序(插入的次序)。因而在使用迭代器遍歷Set時,結果會按元素插入的次序

 LinkedHashSet 綜合了鏈表+哈希表,根據元素的hashCode值來決定元素的存儲位置,它同時使用鏈表維護元素的次序。

當遍歷該集合時候,LinkedHashSet 將會以元素的添加順序訪問集合的元素。

對於 LinkedHashSet 而言,它繼承與 HashSet、又基於 LinkedHashMap 來實現的。

這個相對於HashSet來講有一個很大的不同是LinkedHashSet是有序的。LinkedHashSet在迭代訪問Set中的所有元素時,性能比HashSet好,可是插入時性能稍微遜色於HashSet。

與HashSet相比,特色:

 對集合迭代時,按增長順序返回元素。

 性能略低於HashSet,由於須要維護元素的插入順序。但迭代訪問元素時會有好性能,由於它採用鏈表維護內部順序。

 

LinkedHashSet不容許元素的重複

存儲的順序是元素插入的順序。

 

(13)TreeSet

TreeSet : 保存次序的Set, 底層爲樹結構。使用它能夠從Set中提取有序的序列。

TreeSet 繼承AbstractSet類,實現NavigableSet、Cloneable、Serializable接口。與HashSet是基於HashMap實現同樣,TreeSet 一樣是基於TreeMap 實現的。因爲獲得Tree 的支持,TreeSet 最大特色在於排序,它的做用是提供有序的Set集合。

 

用於對 Set 集合進行元素的指定順序排序,排序須要依據元素自身具有的比較性。

若是元素不具有比較性,在運行時會拋出ClassCastException 異常。 因此元素須要實現Comparable 接口 ,讓元素具有可比較性, 重寫 compareTo 方法 。依據 compareTo 方法的返回值,肯定元素在 TreeSet 數據結構中的位置。 或者用比較器方式,將Comparator對象傳遞給TreeSet構造器來告訴樹集使用不一樣的比較方法

 

TreeSet底層的數據結構就是二叉樹。

     不能寫入空數據

        寫入的數據是有序的。

        不寫入重複數據

 

TreeSet方法保證元素惟一性的方式:就是參考比較方法的結果是否爲0,若是return 0,視爲兩個對象重複,不存。

TreeSet集合排序有兩種方式,Comparable和Comparator區別:

1:讓元素自身具有比較性,須要元素對象實現Comparable接口,覆蓋compareTo方法。

2:讓集合自身具有比較性,須要定義一個實現了Comparator接口的比較器,並覆蓋compare方法,並將該類對象做爲實際參數傳遞給TreeSet集合的構造函數。

 

TreeSet類是SortedSet接口的實現類。由於須要排序,因此性能確定差於HashSet。與HashSet相比,額外增長的方法有:

first():返回第一個元素

last():返回最後一個元素

lower(Object o):返回指定元素以前的元素

higher(Obect o):返回指定元素以後的元素

subSet(fromElement, toElement):返回子集合

能夠定義比較器(Comparator)來實現自定義的排序。默認天然升序排序。

 

TreeSet兩種排序方式:天然排序和定製排序,默認狀況下,TreeSet採用天然排序

 

TreeSet會調用集合元素的compareTo(Object object)方法來比較元素之間的大小關係,而後將元素按升序排列

若是試圖把一個元素添加到TreeSet中,則該對象必須實現Comparable接口實現Comparable接口必須實現compareTo(Object object),兩個對象即經過這個方法進行比較Comparable的典型實現

BigDecimal、BigInteger以及全部的數值類型對應的包裝類型,按對應的數值大小進行比較

Character:按字符的Unicode值進行比較

Boolean:true對應的包裝類實例大於false包裝類對應的實例

String:按字符對應的Unicode值進行比較

Date、Time:後面的時間、日期比前面的時間、日期大

 

向TreeSet中添加一個元素,只有第一個不須要使用compareTo()方法,後面的都要調用該方法

由於只有相同類的兩個實例纔會比較大小,因此向TreeSet中添加的應該是同一個類的對象

 

TreeSet採用紅黑樹的數據結構來存儲集合元素

 

對於TreeSet集合而言,它判斷兩個對象的是否相等的惟一標準是:兩個對象的經過compareTo(Object obj)方法比較是否返回0--若是經過compareTo(Object obj)方法比較返回0,TreeSet則會認爲它們相等,不然認爲它們不相等。對於語句,obj1.compareTo(obj2),若是該方法返回一個正整數,則代表obj1大於obj2;若是該方法返回一個負整數,則代表obj1小於obj2.

 

在默認的compareTo方法中,須要將的兩個的類型的對象的轉換同一個類型,所以須要將的保證的加入到TreeSet中的數據類型是同一個類型,可是若是本身覆蓋compareTo方法時,沒有要求兩個對象強制轉換成同一個對象,是能夠成功的添加treeSet中

 

若是兩個對象經過CompareTo(Object obj)方法比較返回0時,但它們經過equals()方法比較返回false時,TreeSet不會讓第二個元素添加進去

 

(14)Map

Map主要用於存儲健值對,根據鍵獲得值,所以不容許鍵重複,但容許值重複。

Map接口概述:Java.util.Map<k,v>接口:是一個雙列集合

Map集合的特色: 是一個雙列集合,有兩個泛型key和value,使用的時候key和value的數據類型能夠相同。也能夠不一樣.

 Key不容許重複的,value能夠重複的;

 一個key只能對應一個value

底層是一個哈希表(數組+單向鏈表):查詢快,增刪快, 是一個無序集合

 

Map接口中的經常使用方法:

 1.get(key)  根據key值返回對應的value值,key值不存在則返回null

 2.put(key , value); 往集合中添加元素(key和value)

   注意:添加的時候,若是key不存在,返回值null

   若是Key已經存在的話,就會新值替換舊值,返回舊值

 3. remove(key); 刪除key值對應的鍵值對;若是key不存在 ,刪除失敗。返回值爲     null,若是key存在則刪除成功,返回值爲刪除的value

 

Map遍歷方式

第一種方式:經過key找value的方式:

   Map中有一個方法:

      Set <k>  keySet();  返回此映射包含的鍵的Set 集合

   操做步驟:

     1.調用Map集合的中方法keySet,把Map集合中全部的健取出來,存儲到Set集合中

        2.遍歷Set集合,獲取Map集合中的每個健

     3.經過Map集合中的方法get(key),獲取value值

     能夠使用迭代器跟加強for循環遍歷

 第二種方式:Map集合遍歷鍵值方式

    Map集合中的一個方法:

    Set<Map.Entry<k,v>> entrySet(); 返回此映射中包含的映射關係的Set視圖

 使用步驟

     *  1.使用Map集合中的方法entrySet,把鍵值對(鍵與值的映射關係),取出來存儲到Set  集合中

     *  2.遍歷Set集合,獲取每個Entry對象

     *  3.使用Entry對象中的方法getKey和getValue獲取健和值

  能夠使用迭代器跟加強for循環遍歷

 

Collection中的集合元素是孤立的,可理解爲單身,是一個一個存進去的,稱爲單列集合

Map中的集合元素是成對存在的,可理解爲夫妻,是一對一對存進去的,稱爲雙列集合

 

Map中存入的是:鍵值對,鍵不能夠重複,值能夠重複

Map主要用於存儲帶有映射關係的數據(好比學號與學生信息的映射關係)

 

Map沒有繼承Collection接口,Map提供key到value的映射。一個Map中不能包含相同的key,每一個key只能映射一個 value。Map接口提供3種集合的視圖,Map的內容能夠被看成一組key集合,一組value集合,或者一組key-value映射。

Map具備將對象映射到其餘對象的功能,是一個K-V形式存儲容器,你能夠經過containsKey()和containsValue()來判斷集合是否包含某個減或某個值。Map能夠很容以拓展到多維(值能夠是其餘容器甚至是其餘Map):

Map<Object,List<Object>>

Map集合的數據結構僅僅針對鍵有效,與值無關。

 

(15)HashMap

HashMap非線程安全,高效,支持null;

根據鍵的HashCode 來存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度。遍歷時,取得數據的順序是徹底隨機的。

HashMap最多隻容許一條記錄的鍵爲Null;容許多條記錄的值爲 Null。(不容許鍵重複,但容許值重複)

HashMap不支持線程的同步(任一時刻能夠有多個線程同時寫HashMap,即線程非安全),可能會致使數據的不一致。若是須要同步,能夠用 Collections的synchronizedMap() 方法使HashMap具備同步的能力,或者使用ConcurrentHashMap。

Hashtable與 HashMap相似。不一樣的是:它不容許記錄的鍵或者值爲空;它支持線程的同步(任一時刻只有一個線程能寫Hashtable,即線程安全),所以也致使了 Hashtable 在寫入時會比較慢。

HashMap裏面存入的值在取出的時候是隨機的,它根據鍵的HashCode來存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。

HashMap基於哈希表的 Map 接口的實現。此實現提供全部可選的映射操做,並容許使用 null 值和 null 鍵。(除了不一樣步和容許使用 null 以外,HashMap 類與 Hashtable 大體相同。)此類不保證映射的順序,特別是它不保證該順序恆久不變。

值得注意的是HashMap不是線程安全的,若是想要線程安全的HashMap,能夠經過Collections類的靜態方法synchronizedMap得到線程安全的HashMap。

Map map = Collections.synchronizedMap(new HashMap());

 

HashMap的底層主要是基於數組和鏈表來實現的,它之因此有至關快的查詢速度主要是由於它是經過計算散列碼來決定存儲的位置。HashMap中主要是經過key的hashCode來計算hash值的,只要hashCode相同,計算出來的hash值就同樣。若是存儲的對象對多了,就有可能不一樣的對象所算出來的hash值是相同的,這就出現了所謂的hash衝突。學過數據結構的同窗都知道,解決hash衝突的方法有不少,HashMap底層是經過鏈表來解決hash衝突的。

 

HashMap其實也是一個線性的數組實現的,因此能夠理解爲其存儲數據的容器就是一個線性數組。這可能讓咱們很不解,一個線性的數組怎麼實現按鍵值對來存取數據呢?這裏HashMap有作一些處理。首先HashMap裏面實現一個靜態內部類Entry,其重要的屬性有 key , value, next,從屬性key,value咱們就能很明顯的看出來Entry就是HashMap鍵值對實現的一個基礎bean,咱們上面說到HashMap的基礎就是一個線性數組,這個數組就是Entry[],Map裏面的內容都保存在Entry[]裏面。

 

HashMap是經常使用的Java集合之一,是基於哈希表的Map接口的實現。與HashTable主要區別爲不支持同步和容許null做爲key和value。因爲HashMap不是線程安全的,若是想要線程安全,能夠使用ConcurrentHashMap代替。

HashMap的底層是哈希數組,數組元素爲Entry。HashMap經過key的hashCode來計算hash值,當hashCode相同時,經過「拉鍊法」解決衝突

相比於以前的版本,jdk1.8在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(默認爲8)時,將鏈表轉化爲紅黑樹,以減小搜索時間。本來Map.Entry接口的實現類Entry更名爲了Node。轉化爲紅黑樹時改用另外一種實現TreeNode。 

1.8中最大的變化就是在一個Bucket中,若是存儲節點的數量超過了8個,就會將該Bucket中原來以鏈表形式存儲節點轉換爲以樹的形式存儲節點;而若是少於6個,就會還原成鏈表形式存儲。

爲何要這樣作?前面已經說過LinkedList的遍歷操做不太友好,若是在節點個數比較多的狀況下性能會比較差,而樹的遍歷效率是比較好的,主要是優化遍歷,提高性能。

 

HashMap:去掉了contains(),保留了containsKey(),containsValue()

HashMap:key,value能夠爲空.null做爲key只能有一個,null做爲value能夠存在多個

HashMap:使用Iterator

HashMap:數組初始大小爲16,擴容方式爲2的指數冪形式

HashMap:從新計算hash值

 

HashMap是基於哈希表的Map接口的實現,HashMap是一個散列表,存儲的內容是鍵值對(key-value)映射,鍵值對均可爲null;

HashMap繼承自 AbstractMap<K, V> 並實現 Map<K, V>, Cloneable, Serializable接口;

HashMap其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。底層是個數組,數組上存儲的數據是Entry<K,V>類型的鏈表結構對象。

HashMap是無序的,LinkedHashMap和treeMap是有序的;

 

HashMap基於哈希原理,能夠經過put和get方法存儲和獲取對象。當咱們將鍵值對傳遞給put方法時,它調用鍵對象的hashCode()方法來計算hashcode,而後找到對應的bucket位置存儲鍵對象和值對象做爲Map.Entry;若是兩個對象的hashcode相同,因此對應的bucket位置是相同的,HashMap採用鏈表解決衝突碰撞,這個Entry(包含有鍵值對的Map.Entry對象)會存儲到鏈表的下一個節點中;若是對應的hashcode和key值都相同,則修改對應的value的值。HashMap在每一個鏈表節點中存儲鍵值對對象。當使用get()方法獲取對象時,HashMap會根據鍵對象的hashcode去找到對應的bucket位置,找到對應的bucket位置後會調用keys.equals()方法去找到連表中對應的正確的節點找到對象。

 

 HashMap是基於哈希表實現的,每個元素是一個key-value對,其內部經過單鏈表解決衝突問題,容量不足(超過了閥值)時,一樣會自動增加。

 HashMap 實現了Serializable接口,所以它支持序列化,實現了Cloneable接口,能被克隆。

HashMap存數據的過程是:

      HashMap內部維護了一個存儲數據的Entry數組,HashMap採用鏈表解決衝突,每個Entry本質上是一個單向鏈表。當準備添加一個key-value對時,首先經過hash(key)方法計算hash值,而後經過indexFor(hash,length)求該key-value對的存儲位置,計算方法是先用hash&0x7FFFFFFF後,再對length取模,這就保證每個key-value對都能存入HashMap中,當計算出的位置相同時,因爲存入位置是一個鏈表,則把這個key-value對插入鏈表頭。

 

HashMap中key和value都容許爲null。key爲null的鍵值對永遠都放在以table[0]爲頭結點的鏈表中。

HashMap內存儲數據的Entry數組默認是16,若是沒有對Entry擴容機制的話,當存儲的數據一多,Entry內部的鏈表會很長,這就失去了HashMap的存儲意義了。因此HasnMap內部有本身的擴容機制。HashMap內部有:

      變量size,它記錄HashMap的底層數組中已用槽的數量;

      變量threshold,它是HashMap的閾值,用於判斷是否須要調整HashMap的容量(threshold = 容量*加載因子)    

      變量DEFAULT_LOAD_FACTOR = 0.75f,默認加載因子爲0.75

      HashMap擴容的條件是:當size大於threshold時,對HashMap進行擴容  

      擴容是是新建了一個HashMap的底層數組,然後調用transfer方法,將就HashMap的所有元素添加到新的HashMap中(要從新計算元素在新的數組中的索引位置)。 很明顯,擴容是一個至關耗時的操做,由於它須要從新計算這些元素在新的數組中的位置並進行復制處理。所以,咱們在用HashMap的時,最好能提早預估下HashMap中元素的個數,這樣有助於提升HashMap的性能。

 

 加載因子,若是加載因子越大,對空間的利用更充分,可是查找效率會下降(鏈表長度會愈來愈長);若是加載因子過小,那麼表中的數據將過於稀疏(不少空間還沒用,就開始擴容了),對空間形成嚴重浪費。若是咱們在構造方法中不指定,則系統默認加載因子爲0.75,這是一個比較理想的值,通常狀況下咱們是無需修改的。另外,不管咱們指定的容量爲多少,構造方法都會將實際容量設爲不小於指定容量的2的次方的一個數,且最大值不能超過2的30次方。

 

HashMap的初始容量爲16,Hashtable初始容量爲11,二者的填充因子默認都是0.75。

HashMap擴容時是當前容量翻倍即:capacity*2,Hashtable擴容時是容量翻倍+1即:capacity*2+1。

HashMap和Hashtable的底層實現都是數組+鏈表結構實現。

HashMap計算hash對key的hashcode進行了二次hash,以得到更好的散列值,而後對table數組長度取摸:

static int hash(int h) {     

h ^= (h >>> 20) ^ (h >>> 12);     

return h ^ (h >>> 7) ^ (h >>> 4);

}

static int indexFor(int h, int length) {     

return h & (length-1);

}

 

HashMap多線程put操做後,get操做致使死循環。爲什麼出現死循環?

你們都知道,HashMap採用鏈表解決Hash衝突,具體的HashMap的分析由於是鏈表結構,那麼就很容易造成閉合的鏈路,這樣在循環的時候只要有線程對這個HashMap進行get操做就會產生死循環。可是,我好奇的是,這種閉合的鏈路是如何造成的呢。在單線程狀況下,只有一個線程對HashMap的數據結構進行操做,是不可能產生閉合的迴路的。那就只有在多線程併發的狀況下才會出現這種狀況,那就是在put操做的時候,若是size>initialCapacity*loadFactor,那麼這時候HashMap就會進行rehash操做,隨之HashMap的結構就會發生翻天覆地的變化。頗有可能就是在兩個線程在這個時候同時觸發了rehash操做,產生了閉合的迴路。

 

簡單來講,HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要爲了解決哈希衝突而存在的,若是定位到的數組位置不含鏈表(當前entry的next指向null),那麼對於查找,添加等操做很快,僅需一次尋址便可;若是定位到的數組包含鏈表,對於添加操做,其時間複雜度依然爲O(1),由於最新的Entry會插入鏈表頭部,急須要簡單改變引用鏈便可,而對於查找操做來說,此時就須要遍歷鏈表,而後經過key對象的equals方法逐一比對查找。因此,性能考慮,HashMap中的鏈表出現越少,性能纔會越好。

 

HashMap存儲自定義類型:使用HashMap儲存自定義類形式,由於要保證key的惟一性。須要 自定義類重寫  hashCode()跟equals()方法;

HashMap的方法基本都是Map中聲明的方法

實現原理:實現一個哈希表,存儲元素(key/value)時,用key計算hash值,若是hash值沒有碰撞,則只用數組存儲元素;若是hash值碰撞了,則相同的hash值的元素用鏈表存儲;若是相同hash值超過8個,則相同的hash值的元素用紅黑樹存儲。獲取元素時,用key計算hash值,用hash值計算元素在數組中的下標,取得元素若是命中,則返回;若是不是就在紅黑樹或鏈表中找。

PS:存儲元素的數組是有冗餘的。

 

採用了Fail-Fast機制,經過一個modCount值記錄修改次數,在迭代過程當中,判斷modCount跟初始過程記錄的expectedModCount是否相等,若是不相等就表示已經有其餘線程修改了Map,立刻拋出異常;另外擴容過程當中還有可能產生環形鏈表。

synchronized是針對整張Hash表的,即每次鎖住整張表讓線程獨佔

 

(16)HashTable

HashTable線程安全,低效,不支持null ,Hashtable是同步的

HashTable這個類實現了哈希表從key映射到value的數據結構形式。任何非null的對象均可以做爲key或者value。

要在hashtable中存儲和檢索對象,做爲key的對象必須實現hashCode、equals方法。

 

通常來講,默認的加載因子(0.75)提供了一種對於空間、時間消耗比較好的權衡策略。過高的值(指加載因子loadFactor)雖然減小了空間開銷可是增長了檢索時間,這反應在對hashtable的不少操做中,好比get、put方法。

初始容量的控制也是在空間消耗和rehash操做耗時(該操做耗時較大)兩者之間的權衡。 若是初始容量大於哈希表的當前最大的條目數除以加載因子,則不會發生rehash。可是,將初始容量設置太高會浪費空間。

若是有大量的數據須要放進hashtable,則選擇設置較大的初始容量比它自動rehash更優。

 

若是不須要線程安全的實現,建議使用HashMap代替Hashtable

若是想要一個線程安全的高併發實現,那麼建議使用java.util.concurrent.ConcurrentHashMap取代了Hashtable。

HashTable的父類是Dictionary

 

HashTable:線程安全,HashTable方法有synchronized修飾

HashTable:保留了contains(),containsKey(),containsValue()

HashTable:key,value都不能爲空.緣由是源碼中方法裏會遍歷entry,而後用entry的key或者value調用equals(),因此要先判斷key/value是否爲空,若是爲空就會拋出異常

HashTable:使用Enumeration,Iterator

HashTable:數組初始大小爲11,擴容方式爲2*old+1

HashTable: 直接使用hashcode()

Hashtable一樣是基於哈希表實現的,一樣每一個元素是一個key-value對,其內部也是經過單鏈表解決衝突問題,容量不足(超過了閥值)時,一樣會自動增加。

 

Hashtable也是JDK1.0引入的類,是線程安全的,能用於多線程環境中。

Hashtable一樣實現了Serializable接口,它支持序列化,實現了Cloneable接口,能被克隆。

Hashtable計算hash是直接使用key的hashcode對table數組的長度直接進行取模:

int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length;

 

底層數據結構是哈希表,特色和 hashMap 是同樣的

     Hashtable 是線程安全的集合,是單線程的,運行速度慢

     HashMap 是線程不安全的集合,是多線程的,運行速度快

     Hashtable 命運和 Vector 是同樣的,從 JDK1.2 開始,被更先進的 HashMap 取代

     HashMap 容許存儲 null 值,null 健

     Hashtable 不容許存儲 null 值,null 健

     Hashtable 他的孩子,子類 Properties 依然活躍在開發舞臺

 

Properties

Java.util.Properties 集合extends Hashtable<k,v> 集合

 

Properties 集合特色:

Properties集合也是一個雙列集合,key跟value都已經被內置爲String類型

Properties集合是一個惟一和IO流相結合的集合

能夠將集合中存儲的臨時數據,持久化到硬盤的文件中儲存

能夠把文件中儲存對的鍵值對,讀取到集合中使用

 

Properties集合的基本操做:添加數據,遍歷集合,Key和value都已經被內置爲String類型。裏面包含了一些和String類的相關方法

 Object setProperty(String key ,String value) 往集合中添加鍵值對,調用Hashtable的方法put添加

 String getProperty(String key ) 經過key獲取value的值,至關於Map集合中的get(key) 方法

 Set<String >   stringPropertyNames()返回此屬性列表的鍵集。至關於Map集合中的keySet()方法;

 

Properties類的load方法:

    能夠把文件中存儲的鍵值對,讀取到集合中使用

  void load(Reader reader)  

  void load(InputStream inStream)  

  參數:

 Reader reader:字符輸入流,能夠使用FileReader

 InputStream inStream:字節輸入流,能夠使用FileInputStream

 操做步驟:

 1.建立Properties集合對象

 2.建立字符輸入流FileReader對象,構造方法中綁定要讀取的數據源

 3.使用Properties集合中的方法load,把文件中存儲的鍵值對,讀取到集合中使   用

 4.釋放資源

 5.遍歷Properties集合

注意:

 1.流使用Reader字符流,能夠讀取中文數據

 2.流使用InputStream字節流,不能操做中文,會有亂碼

 3.Properties集合的配置文件中,能夠使用註釋單行數據,使用#

 4.Properties集合的配置文件中,key和value默認都是字符串,不用添加""(畫蛇   添足)

 5.Properties集合的配置文件中,key和value的鏈接符號能夠使用=,也能夠使用   空格

 

Properties類的store方法使用:

能夠把集合中存儲的臨時數據,持久化都硬盤的文件中存儲

      void store(Writer writer, String comments)  

                     void store(OutputStream out, String comments)

 參數:

 Writer writer:字符輸出流,能夠使用FileWriter

 OutputStream out:字節輸出流,能夠使用FileOutputStream

 String comments:註釋,解釋說明存儲的文件,不能使用中文(亂碼),默認編碼格式爲   Unicode編碼

能夠使用""空字符串

 操做步驟:

  1.建立Properties集合,往集合中添加數據

  2.建立字符輸出流FileWriter對象,構造方法中綁定要寫入的目的地

  3.調用Properties集合中的方法store,把集合中存儲的臨時數據,持久化都硬盤的文  件中存儲

  4.釋放資源

注意:

  1.流使用Writer字符流,能夠寫入中文數據的

  2.流使用OutputStream字節流,不能操做中文,會有亂碼

  3.Propertie集合存儲的文件,通常都以.properties結尾(程序員默認)

 

(17)LinkeHashMap 

LinkedHashMap繼承自HashMap,實現了Map<K,V>接口。其內部還維護了一個雙向鏈表,在每次插入數據,或者訪問、修改數據時,會增長節點、或調整鏈表的節點順序。以決定迭代時輸出的順序。

默認狀況,遍歷時的順序是按照插入節點的順序。這也是其與HashMap最大的區別。 

也能夠在構造時傳入accessOrder參數,使得其遍歷順序按照訪問的順序輸出。

LinkedHashMap在實現時,就是重寫override了幾個方法。以知足其輸出序列有序的需求。

 

LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先獲得的記錄確定是先插入的

在遍歷的時候會比HashMap慢,不過有種狀況例外:當HashMap容量很大,實際數據較少時,遍歷起來可能會比LinkedHashMap慢。由於LinkedHashMap的遍歷速度只和實際數據有關,和容量無關,而HashMap的遍歷速度和它的容量有關。

 

LinkedHashMap是HashMap的子類,保存了插入的順序,須要輸出的順序和輸入的順序相同時可用LinkedHashMap;

LinkedHashMap 是HashMap的一個子類,若是須要輸出的順序和輸入的相同,那麼用LinkedHashMap能夠實現.

 

LinkedHashMap取鍵值對時,是按照你放入的順序來取的。

LinkedHashMap因爲它的插入有序特性,也是一種比較經常使用的Map集合。它繼承了HashMap,不少方法都直接複用了父類HashMap的方法。本文將探討LinkedHashMap的內部實現,以及它是如何保證插入元素是按插入順序排序的。

在分析前能夠先思考下,既然是按照插入順序,而且以Linked-開頭,就頗有多是鏈表實現。若是純粹以鏈表實現,也不是不能夠,LinkedHashMap內部維護一個鏈表,插入一個元素則把它封裝成Entry節點,並把它插入到鏈表尾部。功能能夠實現,但這帶來的查找效率達到了O(n),顯然遠遠大於HashMap在沒有衝突的狀況下O(1)的時間複雜度。這就絲絕不能體現出Map這種數據結構隨機存取快的優勢。

因此顯然,LinkedHashMap不可能只有一個鏈表來維護Entry節點,它極有可能維護了兩種數據結構:散列表+鏈表。

 

LinkedHashMap的LRU特性

先講一下LRU的定義:LRU(Least Recently Used),即最近最少使用算法,最初是用於內存管理中將無效的內存塊騰出而用於加載數據以提升內存使用效率而發明的算法。

目前已經廣泛用於提升緩存的命中率,如Redis、Memcached中都有使用。

爲啥說LinkedHashMap自己就實現了LRU算法?緣由就在於它額外維護的雙向鏈表中。

在上面已經提到過,在作get/put操做時,LinkedHashMap會將當前訪問/插入的節點移動到鏈表尾部,因此此時鏈表頭部的那個節點就是 "最近最少未被訪問"的節點。

舉個例子:

往一個空的LinkedHashMap中插入A、B、C三個結點,那麼鏈表會經歷如下三個狀態:

1.  A   插入A節點,此時整個鏈表只有這一個節點,既是頭節點也是尾節點

2.  A  ->  B   插入B節點後,此時A爲頭節點,B爲尾節點,而最近最常訪問的節點就是B節點(剛被插入),而最近最少使用的節點就是A節點(相對B節點來說,A節點已經有一段時間沒有被訪問過)

3.  A  ->  B  ->  C  插入C節點後,此時A爲頭節點,C爲尾節點,而最近最常訪問的節點就是C節點(剛被插入),最近最少使用的節點就是A節點 (應該很好理解了吧  : ))

那麼對於緩存來說,A就是我最長時間沒訪問過的緩存,C就是最近才訪問過的緩存,因此當緩存隊列滿時就會從頭開始替換新的緩存值進去,從而保證緩存隊列中的緩存儘量是最近一段時間訪問過的緩存,提升緩存命中率。

 

 LinkedHashMap實現與HashMap的不一樣之處在於,後者維護着一個運行於全部條目的雙重連接列表。此連接列表定義了迭代順序,該迭代順序能夠是插入順序或者是訪問順序。

 

(18)TreeMap

TreeMap實現SortMap接口,可以把它保存的記錄根據鍵排序。

默認是按鍵的升序排序,也能夠指定排序的比較器,當用Iterator 遍歷TreeMap時,獲得的記錄是排過序的。

TreeMap取出來的是排序後的鍵值對。但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好。

TreeMap是基於紅黑樹結構實現的一種Map,要分析TreeMap的實現首先就要對紅黑樹有所瞭解。

 

要了解什麼是紅黑樹,就要了解它的存在主要是爲了解決什麼問題,對比其餘數據結構好比數組,鏈表,Hash表等樹這種結構又有什麼優勢。

treeMap實現了sortMap接口,可以把保存的數據按照鍵的值排序,默認是按照天然數排序也可自定義排序方式。

TreeMap對鍵進行排序了。

 

當用Iterator遍歷TreeMap時,獲得的記錄是排過序的。

若是使用排序的映射,建議使用TreeMap。

在使用TreeMap時,key必須實現Comparable接口或者在構造TreeMap傳入自定義的Comparator,不然會在運行時拋出java.lang.ClassCastException類型的異常。

 

  二叉樹插入元素是有順序的,TreeSet的元素是有序的。

 因爲二叉樹須要對結點排序(插入的結點位置),默認狀況下沒有排序方法,因此元素須要繼承Comparator並重寫compareTo方法來實現元素之間比較大小的功能。

  對於TreeSet,compareTo方法來保證元素的惟一性。【這時候能夠不重寫equals】

 

二叉樹須要結點排序,因此元素之間比較可以比較,因此對於自定義元素對象,須要繼承Comparator並重寫的compareTo方法。 兩個元素相等時,compareTo返回0;左大於右時,返回正整數(通常返回1);小於時返回負整數(通常返回-1)

 

TreeMap的基本操做 containsKey、get、put 和 remove 的時間複雜度是 log(n)

TreeMap中默認的排序爲升序

 

使用entrySet遍歷方式要比keySet遍歷方式快

entrySet遍歷方式獲取Value對象是直接從Entry對象中直接得到,時間複雜度T(n)=o(1);

keySet遍歷獲取Value對象則要從Map中從新獲取,時間複雜度T(n)=o(n);keySet遍歷Map方式比entrySet遍歷Map方式多了一次循環,多遍歷了一次table,當Map的size越大時,遍歷的效率差異就越大。

  

 HashMap經過hashcode對其內容進行快速查找,而TreeMap中全部的元素都保持着某種固定的順序,若是你須要獲得一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。

在Map 中插入、刪除和定位元素,HashMap是最好的選擇。但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好。使用HashMap要求添加的鍵類明肯定義了hashCode()和 equals()的實現。

 

TreeMap 底層數據結構是紅黑樹(一種自平衡的二叉樹) ,其根據比較的返回值是不是0來保證元素惟一性, 元素的排序經過兩種方式:第一種是天然排序(元素具有比較性) 即讓元素所屬的類實現Comparable接口,第二種是比較器排序(集合具有比較性) ,即讓集合接收一個Comparator的實現類對象。

 

Comparable 和 Comparator 的區別:

  Comparable 是一個比較的標準,裏面有比較的方法,對象要具備比較的標準,就必須實現 Comparable 接口;類實現這個接口,就有比較的方法;把元素放到 TreeSet 裏面去,就會自動的調用 CompareTo 方法;可是這個 Comparable  並非專爲 TreeSet 設計的;只是說,TreeSet 順便利用而已;就像 HashCode 和 equals  也同樣,不是專門爲 HashSet 設計同樣;只是你順便利用而已。

  Compartor 是個比較器,也不是專門爲TreeSet設計. 就是一個第三方的比較器接口;若是對象沒有比較性,本身就能夠按照比較器的標準,設計一個比較器,建立一個類,實現這個接口,覆寫方法。

 

(18)Queue

 Queue用於模擬隊列這種數據結構,實現「FIFO」等數據結構。即第一個放進去就是第一個拿出來的元素(從一端進去,從另外一端出來)。隊列常做被看成一個可靠的將對象從程序的某個區域傳輸到另外一個區域的途徑。一般,隊列不容許隨機訪問隊列中的元素。

 

Queue 接口並未定義阻塞隊列的方法,而這在併發編程中是很常見的。BlockingQueue 接口定義了那些等待元素出現或等待隊列中有可用空間的方法,這些方法擴展了此接口。

 

 Queue 實現一般不容許插入 null 元素,儘管某些實現(如 LinkedList)並不由止插入 null。即便在容許 null 的實現中,也不該該將 null 插入到 Queue 中,由於 null 也用做 poll 方法的一個特殊返回值,代表隊列不包含元素。 

 

LinkedList提供了方法以支持隊列的行爲,而且實現了Queue接口。經過LinkedList向上轉型(up cast)爲Queue,看Queue的實現就知道相對於LinkedList,Queue添加了element、offer、peek、poll、remove方法

offer:在容許的狀況下,將一個元素插入到隊尾,或者返回false

peek,element:在不移除的狀況下返回隊頭,peek在隊列爲空返回null,element拋異常NoSuchElementException

poll,remove:移除並返回隊頭,poll當隊列爲空是返回null,remove拋出NoSuchElementException異常

注意:queue.offer在自動包裝機制會自動的把random.nextInt轉化程Integer,把char轉化成Character

 

(19)Deque

Deque是Queue的子接口,咱們知道Queue是一種隊列形式,而Deque則是雙向隊列,它支持從兩個端點方向檢索和插入元素,所以Deque既能夠支持LIFO形式也能夠支持LIFO形式.Deque接口是一種比Stack和Vector更爲豐富的抽象數據形式,由於它同時實現了以上二者.

添加功能

void push(E) 向隊列頭部插入一個元素,失敗時拋出異常

void addFirst(E) 向隊列頭部插入一個元素,失敗時拋出異常

void addLast(E) 向隊列尾部插入一個元素,失敗時拋出異常

boolean offerFirst(E) 向隊列頭部加入一個元素,失敗時返回false

boolean offerLast(E) 向隊列尾部加入一個元素,失敗時返回false

獲取功能

E getFirst() 獲取隊列頭部元素,隊列爲空時拋出異常

E getLast() 獲取隊列尾部元素,隊列爲空時拋出異常

E peekFirst() 獲取隊列頭部元素,隊列爲空時返回null

E peekLast() 獲取隊列尾部元素,隊列爲空時返回null

刪除功能

boolean removeFirstOccurrence(Object) 刪除第一次出現的指定元素,不存在時返回false

boolean removeLastOccurrence(Object) 刪除最後一次出現的指定元素,不存在時返回false

彈出功能

E pop() 彈出隊列頭部元素,隊列爲空時拋出異常

E removeFirst() 彈出隊列頭部元素,隊列爲空時拋出異常

E removeLast() 彈出隊列尾部元素,隊列爲空時拋出異常

E pollFirst() 彈出隊列頭部元素,隊列爲空時返回null

E pollLast() 彈出隊列尾部元素,隊列爲空時返回null

迭代器

Iterator<E> descendingIterator() 返回隊列反向迭代器

 

同Queue同樣,Deque的實現也能夠劃分紅通用實現和併發實現.

  通用實現主要有兩個實現類ArrayDeque和LinkedList.

  ArrayDeque是個可變數組,它是在Java 6以後新添加的,而LinkedList是一種鏈表結構的list,LinkedList要比ArrayDeque更加靈活,由於它也實現了List接口的全部操做,而且能夠插入null元素,這在ArrayDeque中是不容許的.

  從效率來看,ArrayDeque要比LinkedList在兩端增刪元素上更爲高效,由於沒有在節點建立刪除上的開銷.最適合使用LinkedList的狀況是迭代隊列時刪除當前迭代的元素.此外LinkedList多是在遍歷元素時最差的數據結構,而且也LinkedList佔用更多的內存,由於LinkedList是經過鏈表鏈接其整個隊列,它的元素在內存中是隨機分佈的,須要經過每一個節點包含的先後節點的內存地址去訪問先後元素.

  整體ArrayDeque要比LinkedList更優越,在大隊列的測試上有3倍與LinkedList的性能,最好的是給ArrayDeque一個較大的初始化大小,以免底層數組擴容時數據拷貝的開銷.

  LinkedBlockingDeque是Deque的併發實現,在隊列爲空的時候,它的takeFirst,takeLast會阻塞等待隊列處於可用狀態

 

(20)Stack

Stack繼承自Vector,實現一個後進先出的堆棧。Stack提供5個額外的方法使得 Vector得以被看成堆棧使用。基本的push和pop方法,還有peek方法獲得棧頂的元素,empty方法測試堆棧是否爲空,search方法檢測一個元素在堆棧中的位置。Stack剛建立後是空棧。

 

棧,是指「LIFO」先進後出的集合容器,最後一個壓入的元素是第一個出來的,就比如咱們洗碗同樣(或者疊羅漢)第一個擺放的碗放在最下面,天然是最後一個拿出來的。Stack是由LinkedList實現的,做爲Stack的實現,下面是《java編程思想》給出基本的Stack實現:

peek和pop是返回T類型的對象。peek方法提供棧頂元素,但不刪除棧頂,而pop是返回並刪除棧頂元素;

 

(20)ArrayDeque

ArrayDeque類是雙端隊列的實現類,類的繼承結構以下面,繼承自AbastractCollection(該類實習了部分集合通用的方法,其實現了Collection接口),其實現的接口Deque接口中定義了雙端隊列的主要的方法,好比從頭刪除,從尾部刪除,獲取頭數據,獲取尾部數據等等。

public class ArrayDeque<E> extends AbstractCollection<E>                           implements Deque<E>, Cloneable, Serializable

 

ArrayDeque基本特徵

就其實現而言,ArrayDeque採用了循環數組的方式來完成雙端隊列的功能。 

1. 無限的擴展,自動擴展隊列大小的。(固然在不會內存溢出的狀況下。) 

2. 非線程安全的,不支持併發訪問和修改。 

3. 支持fast-fail. 

4. 做爲棧使用的話比比棧要快. 

5. 當隊列使用比linklist要快。 

6. null元素被禁止使用。

 

最小初始化容量限制8(必須是2的冪次)

 

擴容:之因此說該ArrayDeque容量無限制,是由於只要檢測到head==tail的時候,就直接調用doubleCapacity方法進行擴容。

 

刪除元素:刪除元素的基本思路爲肯定那一側的數據少,少的一側移動元素位置,這樣效率相對於不比較更高些,而後,判斷head是跨越最大值仍是爲跨越最大值,繼而能夠分兩種不一樣的狀況進行拷貝。可是該方法比較慢,由於存在數組的拷貝。

 

獲取並刪除元素:這裏在舉個簡單點的例子,中間判斷是否是null,能夠看出該隊列不支持null,經過把其值設爲null就算是將其刪除了。而後head向遞增的方向退一位便可。 

 

ArrayDeque和LinkedList是Deque的兩個通用實現

ArrayDeque不是線程安全的。 

ArrayDeque不能夠存取null元素,由於系統根據某個位置是否爲null來判斷元素的存在。 

看成爲棧使用時,性能比Stack好;看成爲隊列使用時,性能比LinkedList好。 

 

 1.添加元素        addFirst(E e)在數組前面添加元素        addLast(E e)在數組後面添加元素        offerFirst(E e) 在數組前面添加元素,並返回是否添加成功        offerLast(E e) 在數組後天添加元素,並返回是否添加成功  2.刪除元素        removeFirst()刪除第一個元素,並返回刪除元素的值,若是元素爲null,將拋出異常        pollFirst()刪除第一個元素,並返回刪除元素的值,若是元素爲null,將返回null           removeLast()刪除最後一個元素,並返回刪除元素的值,若是爲null,將拋出異常        pollLast()刪除最後一個元素,並返回刪除元素的值,若是爲null,將返回null           removeFirstOccurrence(Object o) 刪除第一次出現的指定元素        removeLastOccurrence(Object o) 刪除最後一次出現的指定元素      3.獲取元素        getFirst() 獲取第一個元素,若是沒有將拋出異常        getLast() 獲取最後一個元素,若是沒有將拋出異常       4.隊列操做        add(E e) 在隊列尾部添加一個元素        offer(E e) 在隊列尾部添加一個元素,並返回是否成功        remove() 刪除隊列中第一個元素,並返回該元素的值,若是元素爲null,將拋出異常(其實底層調用的是removeFirst())           poll()  刪除隊列中第一個元素,並返回該元素的值,若是元素爲null,將返回null(其實調用的是pollFirst())           element() 獲取第一個元素,若是沒有將拋出異常        peek() 獲取第一個元素,若是返回null          5.棧操做        push(E e) 棧頂添加一個元素        pop(E e) 移除棧頂元素,若是棧頂沒有元素將拋出異常            6.其餘        size() 獲取隊列中元素個數        isEmpty() 判斷隊列是否爲空        iterator() 迭代器,從前向後迭代        descendingIterator() 迭代器,從後向前迭代        contain(Object o) 判斷隊列中是否存在該元素        toArray() 轉成數組        clear() 清空隊列        clone() 克隆(複製)一個新的隊列

 

(22)PriorityQueue

咱們知道隊列是遵循先進先出(First-In-First-Out)模式的,但有些時候須要在隊列中基於優先級處理對象。舉個例子,比方說咱們有一個每日交易時段生成股票報告的應用程序,須要處理大量數據而且花費不少處理時間。客戶向這個應用程序發送請求時,實際上就進入了隊列。咱們須要首先處理優先客戶再處理普通用戶。在這種狀況下,Java的PriorityQueue(優先隊列)會頗有幫助。

 

PriorityQueue類在Java1.5中引入並做爲 Java Collections Framework 的一部分。PriorityQueue是基於優先堆的一個無界隊列,這個優先隊列中的元素能夠默認天然排序或者經過提供的Comparator(比較器)在隊列實例化的時排序。

優先隊列不容許空值,並且不支持non-comparable(不可比較)的對象,好比用戶自定義的類。優先隊列要求使用Java Comparable和Comparator接口給對象排序,而且在排序時會按照優先級處理其中的元素。

優先隊列的頭是基於天然排序或者Comparator排序的最小元素。若是有多個對象擁有一樣的排序,那麼就可能隨機地取其中任意一個。當咱們獲取隊列時,返回隊列的頭對象。

優先隊列的大小是不受限制的,但在建立時能夠指定初始大小。當咱們向優先隊列增長元素的時候,隊列大小會自動增長。

PriorityQueue是非線程安全的,因此Java提供了PriorityBlockingQueue(實現BlockingQueue接口)用於Java多線程環境。

因爲知道PriorityQueue是基於Heap的,當新的元素存儲時,會調用siftUpUsingComparator方法

 

 

PriorityQueue的邏輯結構是一棵徹底二叉樹,存儲結構實際上是一個數組。邏輯結構層次遍歷的結果恰好是一個數組。

PriorityQueue優先隊列,它邏輯上使用堆結構(徹底二叉樹)實現,物理上使用動態數組實現,並不是像TreeMap同樣徹底有序,可是若是按照指定方式出隊,結果能夠是有序的。

這裏的堆是一種數據結構而非計算機內存中的堆棧。堆結構在邏輯上是徹底二叉樹,物理存儲上是數組。

徹底二叉樹並非堆結構,堆結構是不徹底有序的徹底二叉樹。

 

(23)BlockingQueue

Java中Queue的最重要的應用大概就是其子類BlockingQueue了。

考慮到生產者消費者模型,咱們有多個生產者和多個消費者,生產者不斷提供資源給消費者,但若是它們的生產/消費速度不匹配或者不穩定,則會形成大量的生產者閒置/消費者閒置。此時,咱們須要使用一個緩衝區來存儲資源,即生產者將資源置於緩衝區,而消費者不斷地從緩衝區中取用資源,從而減小了閒置和阻塞。

BlockingQueue,阻塞隊列,便可視之爲一個緩衝區應用於多線程編程之中。當隊列爲空時,它會阻塞全部消費者線程,而當隊列爲滿時,它會阻塞全部生產者線程。

在queue的基礎上,BlockingQueue又添加了如下方法:

 put:隊列末尾添加一個元素,若隊列已滿阻塞。

 take:移除並返回隊列頭部元素,若隊列已空阻塞。

 drainTo:一次性獲取全部可用對象,能夠用參數指定獲取的個數,該操做是原子操做,不須要針對每一個元素的獲取加鎖。

 

 

(24) ArrayBlockingQueue

由一個定長數組和兩個標識首尾的整型index標識組成,生產者放入數據和消費者取出數據對於ArrayBlockingQueue而言使用了同一個鎖(一個私有的ReentrantLock),於是沒法實現真正的並行。能夠在初始化時除長度參數之外,附加一個boolean類型的變量,用於給其私有的ReentrantLock進行初始化(初始化是否爲公平鎖,默認爲false)。

 

(25) LinkedBlockingQueue

LinkedBlockingQueue的最大特色是,若沒有指定最大容量,其能夠視爲無界隊列(有默認最大容量限制,每每系統資源耗盡也沒法達到)。即,不對生產者的行爲加以限制,只在隊列爲空的時候限制消費者的行爲。LinkedBlockingQueue採用了讀寫分離的兩個ReentrantLock去控制put和take,於是有了更好的性能(相似讀寫鎖提供讀寫場景下更好的性能),以下:

    /** Lock held by take, poll, etc */    private final ReentrantLock takeLock = new ReentrantLock();    /** Wait queue for waiting takes */    private final Condition notEmpty = takeLock.newCondition();    /** Lock held by put, offer, etc */    private final ReentrantLock putLock = new ReentrantLock();    /** Wait queue for waiting puts */    private final Condition notFull = putLock.newCondition();

ArrayBlockingQueue和LinkedBlockingQueue是最經常使用的兩種阻塞隊列。

 

(26) PriorityBlockingQueue

PriorityBlockingQueue是對PriorityQueue的包裝,於是也是一個優先隊列。其優先級默認是直接比較,大者先出隊,也能夠從構造器傳入自定義的Comparator。因爲PriorityQueue從實現上是一個無界隊列,PriorityBlockingQueue一樣是一個無界隊列,對生產者不作限制。

 

(27) DelayQueue

DelayQueue是在PriorityBlockingQueue的基礎上包裝產生的,它用於存放Delayed對象,該隊列的頭部是延遲期滿後保存時間最長的Delayed元素(即,以時間爲優先級利用PriorityBlockingQueue),當沒有元素延遲期滿時,對其進行poll操做將會返回Null。take操做會阻塞。

 

(28) SynchronousQueue

SynchronousQueue十分特殊,它沒有容量——換言之,它是一個長度爲0的BlockingQueue,生產者和消費者進行的是無中介的直接交易,當生產者/消費者沒有找到合適的目標時,即會發生阻塞。但因爲減小了環節,其總體性能在一些系統中可能更加適合。該方法一樣支持在構造時肯定爲公平/默認的非公平模式,若是是非公平模式,有可能會致使某些生產者/消費者飢餓。

 

(29)WeakHashMap

WeakHashMap是一種改進的HashMap,它對key實行「弱引用」,若是一個key再也不被外部所引用,那麼該key能夠被GC回收。

 

(30)EnumSet類

EnumSet是一個專門爲枚舉設計的集合類,EnumSet中的全部元素都必須是指定枚舉類型的枚舉值,該枚舉類型的建立Enumset時顯示會隱式的指定。Enumset的集合元素也是有序的,EnumSet以枚舉值在Enum類內定義的順序來決定集合元素的順序。

 

(31)使用Java8新增的Predicate操做集合

Java 8爲Collection集合新增了removeIf(Predicate filter)方法,該方法將會批量刪除符合條件的filter條件的全部元素

(32)使用java 8 新增的Stream操做集合

Java8新增了Stream、IntStream、LongStream、DoubleStream等流式API,這些API表明了多個支持串行和並行彙集操做的元素,其中Stream是一個通用的流接口,而IntStream、LongStream、DoubleStream則表明了類型爲int,long,double的流。

      獨立使用Stream的步驟以下:

    一、使用Stream或XxxStream的builder()類方法建立該Stream對應的Builder。

     二、重複調用Builder的add()方法向該流中的添加多個元素

     三、調用Builder的build()方法獲取對應的Stream

     四、調用Stream的彙集方法。

 

在Stream中方法分爲兩類中間方法和末端方法

中間方法:中間操做容許流保持打開狀態,並容許直接調用後續方法。上面程序中的map()方法就是中間方法。

末端方法:末端方法是對流的最終操做。當對某個Stream執行末端方法後,該流將會被"消耗"且再也不可用。上面程序中的sum()、count()、average()等方法都是末端方法。

除此以外,關於流的方法還有以下特徵:

有狀態的方法:這種方法會給你流增長一些新的屬性,好比元素的惟一性、元素的最大數量、保證元素的排序的方式被處理等。有狀態的方法每每須要更大的性能開銷

短路方法:短路方法能夠儘早結束對流的操做,沒必要檢查全部的元素。

 

 

版權聲明: 本文有 ```...襉簞點 發表於 bloghome博客

轉載聲明: 可自由轉載、引用,但須要屬名做者且註明文章出處。

文章連接: https://www.bloghome.com.cn/user/yysblog

相關文章
相關標籤/搜索