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