深入淺出 Map 的實現(HashMap、HashTable、LinkedHashMap、TreeMap)

執筆記憶的空白發表於2016-01-27

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、寫入

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		Date date1= new Date();
		for (int i = 0; i < 1000000; i++) {
			map.put(i, i);
		}
		Date date2 = new Date();
		System.out.println("HashMap的插入時間:");
		System.out.println(date2.getTime()-date1.getTime());
		
		Map<Integer, Integer> map1 = new Hashtable<Integer, Integer>();
		Date date3= new Date();
		for (int i = 0; i < 1000000; i++) {
			map1.put(i, i);
		}
		Date date4 = new Date();
		System.out.println("HashTable的插入時間:");
		System.out.println(date4.getTime()-date3.getTime());

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


2、讀取

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		for (int i = 0; i < 10000000; i++) {
			map.put(i, i);
		}
		Date date1= new Date();
		for (Integer key : map.keySet()) {
			map.get(key);
		}
		Date date2 = new Date();
		System.out.println("HashMap的讀取時間:");
		System.out.println(date2.getTime()-date1.getTime());
		
		Map<Integer, Integer> map1 = new Hashtable<Integer, Integer>();
		for (int i = 0; i < 10000000; i++) {
			map1.put(i, i);
		}
		Date date3= new Date();
		for (Integer key : map1.keySet()) {
			map1.get(key);
		}
		Date date4 = new Date();
		System.out.println("HashTable的讀取時間:");
		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的引用,從而在雜湊表的基礎上又構成了雙向連結列表,效果圖如下:




具體程式碼如下:

Map<String, String> map = new LinkedHashMap<String, String>();
		 map.put("aw3", "21f");
		 map.put("dds", "333");
		 map.put("322", "33s");
		 map.put("fes", "ada");
		 map.put("444", "21");
		 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的原始碼

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



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

具體程式碼如下:

//預設的TreeMap升序排列  
        Map<String,Integer> map1 = new TreeMap<String,Integer>();  
          
        map1.put("a", 222);
        map1.put("s", 111);
        map1.put("b", 222);
        map1.put("d", 222); 
        System.out.println("map1=" + map1);  
        
        //自定義排序方式——降序
        Map<String, Integer> map = new TreeMap<String, Integer>(new Comparator<String>() {
            /*
            * int compare(Object o1, Object o2) 返回一個基本型別的整型,
            * 返回負數表示:o1 小於o2,
            * 返回0 表示:o1和o2相等,
            * 返回正數表示:o1大於o2。
            */
            public int compare(String a, String b) {
            	//這裡的compareTo比較的是字串的ASC碼
                return b.compareTo(a);
            }
        });

        map.put("a", 222);
        map.put("s", 111);
        map.put("b", 222);
        map.put("d", 222);
        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型別的時候,可能輸出的結果不是根據數字大小來排序的。例如:


//自定義排序方式——降序
        Map<String, Integer> map2 = new TreeMap<String, Integer>(new Comparator<String>() {
            /*
            * int compare(Object o1, Object o2) 返回一個基本型別的整型,
            * 返回負數表示:o1 小於o2,
            * 返回0 表示:o1和o2相等,
            * 返回正數表示:o1大於o2。
            */
            public int compare(String a, String b) {
            	//這裡的compareTo比較的是字串的ASC碼
                return b.compareTo(a);
            }
        });

        map2.put("1", 222);
        map2.put("5", 111);
        map2.put("22", 222);
        map2.put("19", 222);
        System.out.println("map2=" + map2);

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


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


 //自定義排序方式——降序
        Map<String, Integer> map2 = new TreeMap<String, Integer>(new Comparator<String>() {
            /*
            * int compare(Object o1, Object o2) 返回一個基本型別的整型,
            * 返回負數表示:o1 小於o2,
            * 返回0 表示:o1和o2相等,
            * 返回正數表示:o1大於o2。
            */
            public int compare(String a, String b) {
            	//這裡就是直接比較整型的數值大小
                return Integer.parseInt(b) - Integer.parseInt(a);
            }
        });

        map2.put("1", 222);
        map2.put("5", 111);
        map2.put("22", 222);
        map2.put("19", 222);
        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吧


Map集合強大之處還需要各位的挖掘,如有說錯的地方,還望大家指正,如有補充,也可回覆或私密我





相關文章