[置顶] 深入浅出 Map 的实现(HashMap、HashTable、LinkedHashMap、TreeMap)

来源:互联网 发布:硕放菜鸟网络招聘 编辑:程序博客网 时间:2024/05/20 08:45

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

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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、读取

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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的引用,从而在哈希表的基础上又构成了双向链接列表,效果图如下:




具体代码如下:

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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的源码

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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不能实现的

具体代码如下:

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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类型的时候,可能输出的结果不是根据数字大小来排序的。例如:


[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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的时候,比较的方法就需要改变一下, 如下:


[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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吧


0 0
原创粉丝点击