[置頂] 深入淺出 Map 的實現(HashMap、HashTable、LinkedHashMap、TreeMap)

1、基本介紹

HashMap、TreeMap、HashTable、LinkedHashMap 共同實現了接口Java.util.Map, 都是鍵值對形式,且map的key不允許重複


2、詳細介紹


a、HashMap

是一個最常用的Map實現方式,它根據鍵的HashCode 值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度,但是HashMap是無序、線程不安全的,且HashMap不同步,如果需要線程同步,則可以使用ConcurrentHashMap,也可以使用Collections.synchronizedMap(HashMap map)方法讓HashMap具有同步的能力。其實同步同步,就看有沒有synchronized關鍵字。 HashMap的key 有且只能允許一個null。至於存取方式我就不說了

注:線程不安全(多個線程訪問同一個對象或實現進行更新操作時,造成數據混亂)


b、HashTable

HashTable繼承自Dictionary類 ,它也是無序的,但是HashTable是線程安全的,同步的,即任一時刻只有一個線程能寫HashTable, 但是這也讓HashTable在讀取的時候,速度比HashMap慢,但是寫入速度是比HashMap快的

之前我一直存在一個誤區,以爲HashMap的寫入速度比HashTable快,但是測試表明,HashTable的寫入快,讀取慢。測試結果如下:

1、寫入

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
  2.         Date date1= new Date();  
  3.         for (int i = 0; i < 1000000; i++) {  
  4.             map.put(i, i);  
  5.         }  
  6.         Date date2 = new Date();  
  7.         System.out.println("HashMap的插入時間:");  
  8.         System.out.println(date2.getTime()-date1.getTime());  
  9.           
  10.         Map<Integer, Integer> map1 = new Hashtable<Integer, Integer>();  
  11.         Date date3= new Date();  
  12.         for (int i = 0; i < 1000000; i++) {  
  13.             map1.put(i, i);  
  14.         }  
  15.         Date date4 = new Date();  
  16.         System.out.println("HashTable的插入時間:");  
  17.         System.out.println(date4.getTime()-date3.getTime());  

輸出結果:HashMap的插入時間:1420
                    HashTable的插入時間:797


2、讀取

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
  2.         for (int i = 0; i < 10000000; i++) {  
  3.             map.put(i, i);  
  4.         }  
  5.         Date date1= new Date();  
  6.         for (Integer key : map.keySet()) {  
  7.             map.get(key);  
  8.         }  
  9.         Date date2 = new Date();  
  10.         System.out.println("HashMap的讀取時間:");  
  11.         System.out.println(date2.getTime()-date1.getTime());  
  12.           
  13.         Map<Integer, Integer> map1 = new Hashtable<Integer, Integer>();  
  14.         for (int i = 0; i < 10000000; i++) {  
  15.             map1.put(i, i);  
  16.         }  
  17.         Date date3= new Date();  
  18.         for (Integer key : map1.keySet()) {  
  19.             map1.get(key);  
  20.         }  
  21.         Date date4 = new Date();  
  22.         System.out.println("HashTable的讀取時間:");  
  23.         System.out.println(date4.getTime()-date3.getTime());  

輸出結果:

HashMap的讀取時間:
188
HashTable的讀取時間:
265


c、LinkedHashMap

LinkedHashMap是Map中常用的有序的兩種實現之一, 它保存了記錄的插入順序,先插入的先遍歷到,就是說你插入是什麼順序,你出來就是什麼順序

對於LinkedHashMap而言,它繼承與HashMap、底層使用哈希表與雙向鏈表來保存所有元素。其基本操作與父類HashMap相似,它通過重寫父類相關的方法,來實現自己的鏈接列表特性。LinkedHashMap採用的hash算法和HashMap相同,但是它重新定義了數組中保存的元素Entry,該Entry除了保存當前對象的引用外,還保存了其上一個元素before和下一個元素after的引用,從而在哈希表的基礎上又構成了雙向鏈接列表,效果圖如下:




具體代碼如下:

  1. Map<String, String> map = new LinkedHashMap<String, String>();  
  2.          map.put("aw3""21f");  
  3.          map.put("dds""333");  
  4.          map.put("322""33s");  
  5.          map.put("fes""ada");  
  6.          map.put("444""21");  
  7.          System.out.println("LinkedHashMap的值:" + map);  

輸出結果:LinkedHashMap的值:{aw3=21f, dds=333, 322=33s, fes=ada, 444=21}


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



d、TreeMap

TreeMap實現SortMap接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator 遍歷TreeMap時,得到的記錄是排過序的。

TreeMap的排序原理是:紅黑樹算法的實現 ,具體概念參考:點擊打開鏈接   。

它的主要實現是Comparator架構,通過比較的方式,進行一個排序,以下是TreeMap的源碼

  1. /** 
  2.      * Compares two keys using the correct comparison method for this TreeMap. 
  3.      */  
  4.     final int compare(Object k1, Object k2) {  
  5.         return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)  
  6.             : comparator.compare((K)k1, (K)k2);  
  7.     }  



我們也可以自定義Comparator, TreeMap進行降序排序,這點是LinkedHashMap不能實現的

具體代碼如下:

  1. //默認的TreeMap升序排列    
  2.         Map<String,Integer> map1 = new TreeMap<String,Integer>();    
  3.             
  4.         map1.put("a"222);  
  5.         map1.put("s"111);  
  6.         map1.put("b"222);  
  7.         map1.put("d"222);   
  8.         System.out.println("map1=" + map1);    
  9.           
  10.         //自定義排序方式——降序  
  11.         Map<String, Integer> map = new TreeMap<String, Integer>(new Comparator<String>() {  
  12.             /* 
  13.             * int compare(Object o1, Object o2) 返回一個基本類型的整型, 
  14.             * 返回負數表示:o1 小於o2, 
  15.             * 返回0 表示:o1和o2相等, 
  16.             * 返回正數表示:o1大於o2。 
  17.             */  
  18.             public int compare(String a, String b) {  
  19.                 //這裏的compareTo比較的是字符串的ASC碼  
  20.                 return b.compareTo(a);  
  21.             }  
  22.         });  
  23.   
  24.         map.put("a"222);  
  25.         map.put("s"111);  
  26.         map.put("b"222);  
  27.         map.put("d"222);  
  28.         System.out.println("map=" + map);  

輸出結果:

map1={a=222, b=222, d=222, s=111}
map={s=111, d=222, b=222, a=222}


注:這裏字符串的compareTo 值得注意,因爲比較的是ASC碼,所以當字符串裏面的值爲int類型的時候,可能輸出的結果不是根據數字大小來排序的。例如:


  1. //自定義排序方式——降序  
  2.         Map<String, Integer> map2 = new TreeMap<String, Integer>(new Comparator<String>() {  
  3.             /* 
  4.             * int compare(Object o1, Object o2) 返回一個基本類型的整型, 
  5.             * 返回負數表示:o1 小於o2, 
  6.             * 返回0 表示:o1和o2相等, 
  7.             * 返回正數表示:o1大於o2。 
  8.             */  
  9.             public int compare(String a, String b) {  
  10.                 //這裏的compareTo比較的是字符串的ASC碼  
  11.                 return b.compareTo(a);  
  12.             }  
  13.         });  
  14.   
  15.         map2.put("1"222);  
  16.         map2.put("5"111);  
  17.         map2.put("22"222);  
  18.         map2.put("19"222);  
  19.         System.out.println("map2=" + map2);  

輸出結果:map2={5=111, 22=222, 19=222, 1=222}


也就是說,當你用Integer做key的時候,比較的方法就需要改變一下, 如下:


  1. //自定義排序方式——降序  
  2.        Map<String, Integer> map2 = new TreeMap<String, Integer>(new Comparator<String>() {  
  3.            /* 
  4.            * int compare(Object o1, Object o2) 返回一個基本類型的整型, 
  5.            * 返回負數表示:o1 小於o2, 
  6.            * 返回0 表示:o1和o2相等, 
  7.            * 返回正數表示:o1大於o2。 
  8.            */  
  9.            public int compare(String a, String b) {  
  10.             //這裏就是直接比較整型的數值大小  
  11.                return Integer.parseInt(b) - Integer.parseInt(a);  
  12.            }  
  13.        });  
  14.   
  15.        map2.put("1"222);  
  16.        map2.put("5"111);  
  17.        map2.put("22"222);  
  18.        map2.put("19"222);  
  19.        System.out.println("map2=" + map2);  

輸出結果: map2={22=222, 19=222, 5=111, 1=222}



3、總結

1、Map中,HashMap具有超高的訪問速度,如果我們只是在Map 中插入、刪除和定位元素,而無關線程安全或者同步問題,HashMap 是最好的選擇。

2、如果考慮線程安全或者寫入速度的話,可以使用HashTable

3、如果想按怎麼存的順序怎麼取,比如隊列形式,排排隊。 那麼使用LinkedHashMap吧,怎麼用怎麼爽

4、如果需要讓Map按照key進行升序或者降序排序,那就用TreeMap吧


發佈了11 篇原創文章 · 獲贊 30 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章