1. 程式人生 > >java中的java.util包的結構

java中的java.util包的結構

之前聽一位老師講過,學Java的程式設計師,lang包和util包至少是要過一遍的。 
很慚愧的是,從入門到現在,我還沒完整的探究過這兩個基礎包。 
今天藉著跟公司小夥伴分享的機會,把util包簡單的梳理一遍。由於最近加班很多,此篇先做粗略總結,日後有時間再完善。

1. util包的框架
常用的集合類主要實現兩個“super介面”而來:Collection和Map。

1.1 Collection有兩個子介面:List和Set


List特點是元素有序,且可重複。實現的常用集合類有ArrayList、LinkedList,和Vector(執行緒安全)。

Set特點是元素無序,不可重複。實現的常用集合類有HashSet,LinkedHashSet,TreeSet(可排序)

1.2 Map是key、value鍵值對的集合


特點是key值無序不可重複,value值可重複(這樣表述其實不太準確,因為實際上key和value是繫結在一起的)。常用的有HashMap,HashTable(執行緒安全),TreeMap(可排序)。

1.3 其餘重要介面和類
上面是util包中的集合框架,一般Java教材裡面都會講到。但我們深入研究一下,會發現還有其餘幾個重要的內容:

Iterator:迭代介面 
集合類實現該介面後便具有了迭代功能。最簡單的迭代實現是ArrayList,迭代過程其實就是陣列的迭代。LinkedList、LinkedHashSet和LinkedHashMap迭代過程就是連結串列的迭代。這兩者的迭代效率都很高,迭代時間與容器裡的元素數目成正比。但HashSet、HashMap迭代效率就略低了,因為採用了雜湊表,所以元素是雜湊在陣列中的,迭代時必須讀完整個陣列,迭代時間與容器的容量成正比。
Comparator:比較介面 
實現該介面後,集合內元素便可比較通過compare()方法實現元素排序
AbstractXXX:骨架類 
所謂骨架類,其實就是不同集合的核心程式碼實現,讓繼承這個抽象類的子類少乾點活。例如AbstarctList代表“隨機訪問”集合(底層陣列實現)的骨幹程式碼實現。AbstractSequentialList代表“連續訪問”(底層連結串列實現)集合的骨幹程式碼實現。
Collections、Arrays 
集合工具類和陣列工具類。Java中的工具類好像都喜歡在對應的介面或類名稱後,加S來表示其工具類。
接下來給一張比較完整的util包框架圖:

2. 常用集合類原理
2.1 ArrayList
ArrayList的實現最簡單,採用的順序表,底層就是一個Object陣列,初始容量為10,每當元素要超過容量時,重新建立一個更大的陣列,並把原資料拷到新陣列中來。

2.2 LinkedList
LinkedList採用雙向連結串列。集合中的每一個元素都會有兩個成員變數prev和next,分別指向它的前一元素和後一元素。

ArrayList和LinkedList的區別這裡就不詳細討論了,其實就是順序表和連結串列兩種資料結構的區別。之前寫的博文中已經提到(包括ArrayList和LinkedList的詳細實現): 
資料結構基礎(一)線性表

2.3 Vector
Vector底層實現和ArrayList類似,區別在於在許多方法上加了synchronized關鍵字,來實現了多執行緒安全。但代價是效能的降低。由於加鎖的是整個集合,所以併發情況下進行迭代會鎖住很長時間。

2.4 HashMap
HashMap採用的是雜湊表結構,用連結串列法來解決hash衝突。這裡不詳細討論,之前的文章寫過: 
HashMap原理解析

2.5 HashTable
HashTable的底層實現和HashMap類似,區別也是在許多方法上加了synchronized關鍵字,來實現了多執行緒安全。

2.6 LinkedHashMap
在HashMap的基礎上加了雙鏈表,該集合中的每個元素也都保留了前一個元素和後一個元素的“指標”。這樣便可以按照插入順序來讀取集合元素。也可設定為按照訪問順序來讀取集合元素。 
由於要維護額外的雙鏈表,LinkedHashMap增刪操作會比HashMap慢,但迭代時會比HashMap快。

2.7 TreeMap
採用了紅黑樹資料結構,從而實現了有序集合。這個比較複雜,以後單獨開出一篇來討論,此處略。

2.8 HashSet、LinkedHashSet、TreeSet
Set和Map有千絲萬縷的聯絡呀。例如HashSet底層實現其實就是一個固定value的HashMap。LinkedHashSet就是一個value固定的LinkedHashMap,TreeSet就是一個value固定的TreeMap。

3. 集合的併發
3.1 併發類的選擇
講到併發的集合,一般都想到util包中的兩個類:HashTable和Vector。然而實際使用情況中,並不推薦使用這兩個類。

首先,HashTable和Vector是從JDK1.0便存在的“古老”類,當時Collection、Map介面都還沒。這樣導致的問題是,當後來HashTable和Vector實現Map,Collection介面時,出現了許多無用而重複的方法。例如Vector原本有一個addElement()的方法,當實現了Collection介面後,又出現了一個add()方法。而實際上,這兩個方法一模一樣。

替代的這兩個併發類的常見方法是Collections.synchronizedXXX(…),這個方法可以把ArrayList,HashMap等集合變為執行緒安全的集合類。

那麼,Vector和Collections.synchronizedXXX(…)的底層實現有什麼區別呢?

我們來看看兩者的add()方法實現:

    //Vector
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

    //Collections.SynchronizedList
    public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
1
2
3
4
5
6
7
8
9
10
11
12
可以看出, 兩者實現多執行緒的方式都是對集合的方法加鎖,區別在於,Vector是對方法加鎖,鎖的是本物件,而Collections.synchronizedXXX(…)是對一個變數加鎖。區別並不大。

那麼,既然Collections.synchronizedXXX(…)比較好,用它創建出執行緒安全的集合類是不是就一勞永逸的滿足我們所有的需求了呢?很不幸,不完全是。

Collections.synchronizedXXX(…)和HashTable、Vector在高併發時都有著很大的效能缺陷。因為它們的增、刪、取都會鎖住整個集合。想一想,一個執行緒在迭代十萬個元素的Vector,其餘執行緒對集合的操作時不時就阻塞了,受到了多大的影響啊。

為了解決這兩種方法在高併發下的效能的低下。我們查詢一下Java的API,發現在java.util.concurrent裡面有許多針對高併發設計的類,例如:CopyOnWriteArrayList和ConcurrentHashMap。

ConcurrentHashMap的優化原理在於,採用了Segment的機制:

可以看成,ConcurrentHashMap底層每一個Segment都是一個HashMap,這樣增刪取時只需要鎖住一段的Segment,而不是整個集合。從而優化了高併發下的效能。

CopyOnWriteArrayList主要是對高併發下的讀、迭代做優化。實現原理在於每次add,remove操作都是重新建立一個新的陣列,等操作結束再把引用指向新的陣列。add,remove都是加了鎖的,而get方法沒有加鎖,因為每次迭代時都是在舊的陣列上迭代。所以CopyOnWriteArrayList適用於讀多寫少的併發場景。

3.2 迭代fail-fast機制
之前寫的博文:Java迭代foreach原理解析(java.util.ConcurrentModificationException的原因)
--------------------- 
作者:大樹先生 
來源:CSDN 
原文:https://blog.csdn.net/z55887/article/details/57587632 
版權宣告:本文為博主原創文章,轉載請附上博文連結!