java中Map有哪些實現類和使用場景


Java中的map是一個很重要的集合(集合是用來存放對象的,集合主要分為Collection和Map兩個接口),他是一個接口,下面有多個實現類,這些類各有千秋,各自有各自的優點和缺點。

 

     map的主要特點是鍵值對的形式,一一對應,且一個key只對應1個value,且key唯一。其常用的map實現類主要有HashMap、HashTable、TreeMap、ConcurrentHashMap、LinkedHashMap、weakHashMap等等。

1、HashMap

      使用哈希表實現(最近的jdk1.8改用紅黑樹存儲而非哈希表),它是線程不安全的Map,方法上都沒有synchronized關鍵字修飾,具體可以參考https://blog.csdn.net/qq_30683329/article/details/80454518

2、HashTable

       hashTable是線程安全的一個map實現類,它實現線程安全的方法是在各個方法上添加了synchronized關鍵字。但是現在已經不再推薦使用HashTable了,因為現在有了ConcurrentHashMap這個專門用於多線程場景下的map實現類,其大大優化了多線程下的性能。

3、ConcurrentHashMap

       這個map實現類是在jdk1.5中加入的,其在jdk1.6/1.7中的主要實現原理是segment段鎖,它不再使用和HashTable一樣的synchronized一樣的關鍵字對整個方法進行加鎖,而是轉而利用segment段落鎖來對其進行加鎖,以保證Map的多線程安全。

     其實可以理解為,一個ConcurrentHashMap是由多個HashTable組成,所以它允許獲取不用段鎖的線程同時持有該資源,segment有多少個,理論上就可以同時有多少個線程來持有它這個資源。

     其默認的segment是一個數組,默認長度為16。也就是說理論上可以提高16倍的性能。

      但是要注意咯,在JAVA的jdk1.8中則對ConcurrentHashMap又再次進行了大的修改,取消了segment段鎖字段,采用了CAS+Synchronized技術來保障線程安全。底層采用數組+鏈表+紅黑樹的存儲結構,也就是和HashMap一樣。這里注意Node其實就是保存一個鍵值對的最基本的對象。其中Value和next都是使用的volatile關鍵字進行了修飾,以確保線程安全。這里推薦一下大神的Volatile的深入理解篇,寫的非常好http://www.cnblogs.com/xrq730/p/7048693.html。

     在插入元素時,會首先進行CAS判定,如果OK就插入其中,並將size+1,但是如果失敗了,就會通過自旋鎖自旋后再次嘗試插入,直到成功。

    所謂的CAS也就是compare And Swap(比較和交換),即在更改前先對內存中的變量值和你指定的那個變量值進行比較,如果相同這說明在這期間沒有被修改過,則可以進行修改,而如果不一樣了,則就要停止修改,否則就會覆蓋掉其他的參數。即內存值a,舊值b,和要修改的值c,如果這里a=b,那么就可以進行更新,就可以將內存值a修改成c。否則就要終止該更新操作。

    為什么這里會用volatile進行修飾,我在其他博客找到了答案。主要有兩個用處:

1、令這個被修飾的變量的更新具有可見性,一旦該變量遭到了修改,其他線程立馬就會知道,立馬放棄自己在自己工作內存中持有的該變量值,轉而重新去主內存中獲取到最新的該變量值。

2、產生了內存屏障,這里volatile可以保證CPU在執行代碼時保證,所有被volatile中被修飾的之前的一定在之前被執行,也就是所謂的“指令重排序”。

    同hashMap一樣,在JDK1.8中,如果鏈表中存儲的Entry超過了8個則就會自動轉換鏈表為紅黑樹,提高查詢效率。

4、TreeMap

     TreeMap也是一個很常用的map實現類,因為他具有一個很大的特點就是會對Key進行排序,使用了TreeMap存儲鍵值對,再使用iterator進行輸出時,會發現其默認采用key由小到大的順序輸出鍵值對,如果想要按照其他的方式來排序,需要重寫也就是override 它的compartor接口。此處引用一下其他大神的代碼:


1 import java.util.Comparator;
2 import java.util.Iterator;
3 import java.util.Set;
4 import java.util.TreeMap;
5
6
7 public class Compare {
8 public static void main(String[] args) {
9 TreeMap<String,Integer> map = new TreeMap<String,Integer>(new xbComparator());
10 map.put("key_1", 1);
11 map.put("key_2", 2);
12 map.put("key_3", 3);
13 Set<String> keys = map.keySet();
14 Iterator<String> iter = keys.iterator();
15 while(iter.hasNext())
16 {
17 String key = iter.next();
18 System.out.println(" "+key+":"+map.get(key));
19 }
20 }
21 }
22 class xbComparator implements Comparator
23 {
24 public int compare(Object o1,Object o2)
25 {
26 String i1=(String)o1;
27 String i2=(String)o2;
28 return -i1.compareTo(i2);
29 }
30 }

    另外,TreeMap底層的存儲結構也是一顆紅黑樹。是不是發現好多都是紅黑樹,沒錯因為紅黑樹查找效率高,只有O(lgn)。它是一種自平衡的二叉查找樹。在每次插入和刪除節點時,都可以自動調節樹結構,以保證樹的高度是lgn。

5、LinkedHashMap

    LinkedHashMap它的特點主要在於linked,帶有這個字眼的就表示底層用的是鏈表來進行的存儲。相對於其他的無序的map實現類,還有像TreeMap這樣的排序類,linkedHashMap最大的特點在於有序,但是它的有序主要體現在先進先出FIFIO上。沒錯,LinkedHashMap主要依靠雙向鏈表和hash表來實現的。

 

 

    仔細看,這里雖然在計算hashcode時還是發生了hash沖突,采用了鏈地址法解決了沖突,但是這里的Entry對象是采用雙向鏈表保存的,每個Entry都有一個after和before的屬性。當插入一個entry時,如果發生了沖突,就可以將新的Entry插入Entry鏈表中的頭部,但是按照雙向鏈表的角度來說,又會將該Entry插入到雙向鏈表的尾部。

6、weakHashMap

    首先,weakHashMap它是一個“弱鍵”,它的Key值和Value都可以是null,而且其Map中如果這個Key值指向的對象沒被使用,此時觸發了GC,該對象就會被回收掉的。其原理主要是使用的WeakReference和ReferenceQueue實現的,其key就是weakReference,而ReferenceQueue中保存了被回收的 Key-Value。

    如果當其中一個Key-Value不再使用被回收時,就將其加入ReferenceQueue隊列中。當下次再次調用該WeakHashMap時,就會去更新該map,比如ReferenceQueue中的key-value,將其中包含的key-value全部刪除掉。這就是所謂的“自動刪除”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM