1.Java的集合類 2.Vector,ArrayList,LinkedList,SynchronizedList 3.HashMap與ConcurrentHashMap的區別

在Java的java.util包中,有一批類被稱爲集合類

集合類,顧名思義,就是存放對象的集合的類,可以是數組形式【ArrayList】,也可以是鏈表形式【LinkedList】,也可以是兩種形式結合【HashMap】。

Java的集合類的父類是Collection接口,主要分爲三大類:List、Map、Set。

其中,List是一組元素的集合,最常用到的爲ArrayList、LinkedList;

Map是一組成對對象【鍵值對】的集合,根據Key來存取Value,最常用到的爲HashMap和LinkedList;

Set是一個真正數學意義上的元素的集合,其中不包含重複的元素,最常用到的爲HashSet和TreeSet。



其中,在JDK的源碼中,Set的底層實現都是使用對應的Map來實現的,將存取於Set中的元素作爲Map的Key,使用同一個對象作爲Value。例如,HashSet中是使用了一個HashMap來作爲內部存儲實現,



TreeSet也是如此,內部使用了一個TreeMap來存放元素,圖就不貼了。


ArrayList和LinkedList的區別前面已經寫過博客。

HashMap和TreeMap的區別:

HashMap是基於散列表實現,內部存儲結構爲鄰接表【數組+鏈表】;

TreeMap是基於紅黑樹實現,內部存儲結構爲紅黑樹。

因此存入HashMap的鍵值對要求key必須提供hashCode()方法作爲散列依據,此方法爲Object類中已有;

而對於TreeMap來說,要麼在初始化TreeMap時,提供一個Comparator作爲比較鍵大小的依據,或者要求key的類必須實現Comparable。如果這兩個條件都沒有滿足,那向Treemap中put的時候就會報出異常。


==================================強大的分割線======================================


總體來說,Vector和ArrayList是一致的,實現的接口也是完全一樣,內部的存儲結構也基本一致【都是數組】,每個方法的實現也幾乎相同。

區別在於,Vector是線程安全的,每個方法都加了synchronized修飾符,而ArrayList沒有。

而且Vector是JDK1.0引入的,而ArrayList是JDK1.2引入的。




==================================強大的分割線======================================


再來說ArrayList和LinkedList的區別,上面說過ArrayList內部是數組實現的,LinkedList內部是鏈表實現的。

因此,當遇到讀取比較多,插入、刪除比較少的時候,推薦使用ArrayList,畢竟數組讀取速度飛快,插入刪除速度需要移動大量元素;而當遇到插入刪除比較多的時候,推薦使用LinkedList。


==================================強大的分割線======================================


在Java集合工具類Collections中,提供了一個Collections.synchronizedList方法,可以傳入一個List對象,返回出一個SynchronizedList。

SynchronizedList只是提供了一個對List對象的封裝,對List的每個操作都添加了synchronized修飾,基本上與Vector一致,只是用法不同而已。比如現在已經有個LinkedList,如果想要一個線程安全的List,只需執行Collections.synchronized(linkedList)即可,沒有任何的元素拷貝操作,此時,如果用Vector實現,則必須遍歷LinkedList,將其中的每一個元素拷貝到Vector中。



==================================強大的分割線======================================



從JDK1.2起,就有了HashMap,正如前一篇文章所說,HashMap不是線程安全的,因此多線程操作時需要格外小心。

在JDK1.5中,偉大的Doug Lea給我們帶來了concurrent包,從此Map也有安全的了。



ConcurrentHashMap具體是怎麼實現線程安全的呢,肯定不可能是每個方法加synchronized,那樣就變成了HashTable。

從ConcurrentHashMap代碼中可以看出,它引入了一個“分段鎖”的概念,具體可以理解爲把一個大的Map拆分成N個小的HashTable,根據key.hashCode()來決定把key放到哪個HashTable中。

在ConcurrentHashMap中,就是把Map分成了N個Segment,put和get的時候,都是現根據key.hashCode()算出放到哪個Segment中:






測試程序:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. import java.util.concurrent.ConcurrentHashMap;  
  2.   
  3. public class ConcurrentHashMapTest {  
  4.       
  5.     private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>();  
  6.     public static void main(String[] args) {  
  7.         new Thread("Thread1"){  
  8.             @Override  
  9.             public void run() {  
  10.                 map.put(333);  
  11.             }  
  12.         };  
  13.           
  14.         new Thread("Thread2"){  
  15.             @Override  
  16.             public void run() {  
  17.                 map.put(444);  
  18.             }  
  19.         };  
  20.           
  21.         new Thread("Thread3"){  
  22.             @Override  
  23.             public void run() {  
  24.                 map.put(777);  
  25.             }  
  26.         };  
  27.         System.out.println(map);  
  28.     }  
  29. }  

ConcurrentHashMap中默認是把segments初始化爲長度爲16的數組。

根據ConcurrentHashMap.segmentFor的算法,3、4對應的Segment都是segments[1],7對應的Segment是segments[12]。

(1)Thread1和Thread2先後進入Segment.put方法時,Thread1會首先獲取到鎖,可以進入,而Thread2則會阻塞在鎖上:


(2)切換到Thread3,也走到Segment.put方法,因爲7所存儲的Segment和3、4不同,因此,不會阻塞在lock():



以上就是ConcurrentHashMap的工作機制,通過把整個Map分爲N個Segment(類似HashTable),可以提供相同的線程安全,但是效率提升N倍,默認提升16倍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章