在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。
==================================強大的分割線======================================
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中:
測試程序:
- import java.util.concurrent.ConcurrentHashMap;
- public class ConcurrentHashMapTest {
- private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>();
- public static void main(String[] args) {
- new Thread("Thread1"){
- @Override
- public void run() {
- map.put(3, 33);
- }
- };
- new Thread("Thread2"){
- @Override
- public void run() {
- map.put(4, 44);
- }
- };
- new Thread("Thread3"){
- @Override
- public void run() {
- map.put(7, 77);
- }
- };
- System.out.println(map);
- }
- }
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倍。