[置顶] 深入浅出 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、写入

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集合强大之处还需要各位的挖掘,如有说错的地方,还望大家指正,如有补充,也可回复或私密我





你可能感兴趣的:(map,HashMap,LinkedHashMap,TreeMap,Hashtable)