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吧