java集合类源码分析之Map(二)
这一节主要讨论Map接口的几个实现类的区别和用法。
1.线程安全
Hashtable是线程安全的(原因与StringBuffer、Vector相似),而其他的Map实现类都是非线程安全的,至于为什么,可以看之前的一些文章,前面已经介绍地很详细了。
这里特别讲解一下HashMap和Hashtable的区别与联系:
- 继承的父类不同。Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
- 线程安全性不同。
- key和value是否允许null值。 Hashtable中,key和value都不允许出现null值,HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
- 遍历的实现方式不同。Hashtable、HashMap都实现了 Iterator,而由于历史原因,Hashtable还使用Enumeration的方式 。
- 哈希值不同。Hashtable直接使用对象的hashCode,而HashMap重新计算hash值。
- 数组初始化和扩容方式不同。 Hashtable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。所以,Hashtable扩容时,将容量变为原来的2倍加1(2*old+1),而HashMap扩容时,将容量变为原来的2倍。
2.适用条件
HashMap:适用于需要快速查找的数据,并且数据无序的情况下,允许键为空(只一次),允许值为空;
TreeMap:适用于按key有序存储的数据,不允许键为空;
Hashtable:适用于有同步要求的情况下,保证线程安全,不允许键和值为空;
LinkedHashMap:保存了元素插入时的顺序,具有HashMap的所有特性,但遍历时比HashMap要慢。
HashMap与LinkedHashMap的比较示例:
1 private static void linkedHashMapAnal() { 2 //LinkedHashMap具有HashMap的所有特性,并且能够保留元素插入时的顺序 3 Map<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>(); 4 Map<String, Integer> hashMap = new HashMap<String, Integer>(); 5 6 hashMap.put("A", 120); 7 hashMap.put("B", 100); 8 hashMap.put("C", 105); 9 hashMap.put("D", 200); 10 for(String key: hashMap.keySet()){ 11 System.out.print(key+":"+hashMap.get(key)+" "); 12 }//D:200 A:120 B:100 C:105 13 14 System.out.println(); 15 linkedHashMap.put("A", 120); 16 linkedHashMap.put("B", 100); 17 linkedHashMap.put("C", 105); 18 linkedHashMap.put("D", 200); 19 for(String key: linkedHashMap.keySet()){ 20 System.out.print(key+":"+linkedHashMap.get(key)+" "); 21 }//A:120 B:100 C:105 D:200 22 }
3.排序问题
Map集合的排序常常分为按key排序和按value排序。
- 按key排序
有两种方式来实现Map的按key排序:TreeMap中的比较器和Collections类中的sort方法。
1.TreeMap的按key排序
1 //采用默认比较器按key值升序排列,进行存储 2 Map<String, Integer> treeMap = new TreeMap<String, Integer>(); 3 treeMap.put("B", 120); 4 treeMap.put("C", 105); 5 treeMap.put("A", 100); 6 for(String key: treeMap.keySet()){ 7 System.out.print(key+":"+treeMap.get(key)+", "); 8 } //A:100, B:120, C:105, 9 }
1 //自定义比较器,实现降序存储 2 Map<String, Integer> treeMap = new TreeMap<String, Integer>(new Comparator<String>() { 3 @Override 4 public int compare(String o1, String o2) { 5 // return 0; 默认升序 6 return o2.compareTo(o1); //降序 7 } 8 }); 9 treeMap.put("B", 120); 10 treeMap.put("C", 105); 11 treeMap.put("A", 100); 12 for(String key: treeMap.keySet()){ 13 System.out.print(key+":"+treeMap.get(key)+", "); 14 } //C:105, B:120, A:100,
1 //用SortedMap子集初始化TreeMap对象,调用参数自带的比较器 2 //注意:TreeMap初始化的参数必须为SortedMap类型或者其实现类TreeMap类型,否则将调用默认比较器,所以此处也可以创建一个TreeMap类型的subMap 3 SortedMap<String, Integer> subMap = new TreeMap<String, Integer>(new Comparator<String>() { 4 5 @Override 6 public int compare(String o1, String o2) { 7 // TODO Auto-generated method stub 8 return o2.compareTo(o1); 9 } 10 }); 11 subMap.put("B", 100); 12 subMap.put("F", 120); 13 subMap.put("D", 105); 14 for(String key: subMap.keySet()){ 15 System.out.print(key+":"+subMap.get(key)+", "); 16 } //F:120, D:105, B:100, 17 18 System.out.println(); 19 20 Map<String, Integer> treeMap = new TreeMap<String, Integer>(subMap); 21 for(String key: treeMap.keySet()){ 22 System.out.print(key+":"+treeMap.get(key)+", "); 23 } //F:120, D:105, B:100, 24 25 System.out.println(); 26 27 treeMap.put("A", 200); 28 treeMap.put("C", 300); 29 treeMap.put("E", 400); 30 for(String key: treeMap.keySet()){ 31 System.out.print(key+":"+treeMap.get(key)+", "); 32 } //F:120, E:400, D:105, C:300, B:100, A:200,
2.HashMap的按key排序
1 //用HashMap子集初始化TreeMap对象,调用默认比较器(升序) 2 //注意:TreeMap初始化的参数为Map接口的实现类subMap 3 Map<String, Integer> subMap = new HashMap<String, Integer>(); 4 subMap.put("B", 100); 5 subMap.put("A", 120); 6 subMap.put("C", 105); 7 subMap.put("D", 200); 8 for(String key: subMap.keySet()){ 9 System.out.print(key+":"+subMap.get(key)+", "); 10 } //D:200, A:120, B:100, C:105, 11 12 System.out.println(); 13 14 Map<String, Integer> treeMap = new TreeMap<String, Integer>(subMap); 15 for(String key: treeMap.keySet()){ 16 System.out.print(key+":"+treeMap.get(key)+", "); 17 } //A:120, B:100, C:105, D:200,
1 Map<String, Integer> hashMap = new HashMap<String, Integer>(); 2 3 //hashMap默认按key的哈希值排列,是一种无序存储 4 hashMap.put("A", 100); 5 hashMap.put("B", 120); 6 hashMap.put("C", 105); 7 hashMap.put("D", 100); 8 for(String key: hashMap.keySet()){ 9 System.out.print(key+":"+hashMap.get(key)+" "); 10 }//D:100 A:100 B:120 C:105 11 12 System.out.println(); 13 //将HashMap对象存储到List集合中 14 ArrayList<Map.Entry<String, Integer>> mapList = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet()); 15 //调用Collections的sort方法,并且重写比较器 16 Collections.sort(mapList, new Comparator<Entry<String, Integer>>() { 17 18 @Override 19 public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { 20 // TODO Auto-generated method stub 21 return o1.getKey().compareTo(o2.getKey());//升序,按value排序只需将getKey换成getValue即可 22 // return o2.getKey().compareTo(o1.getKey());//降序,按value排序只需将getKey换成getValue即可 23 } 24 } ); 25 26 for (int i = 0; i < mapList.size(); i++) { 27 System.out.print(mapList.get(i).getKey()+":"+mapList.get(i).getValue()+" "); 28 }//A:100 B:120 C:105 D:100
- 按value排序
有了上面的例子,我们知道也可以通过Collections的sort方法来进行Map集合的value排序,包括HashMap和TreeMap。
1 Map<String, Integer> hashMap = new HashMap<String, Integer>(); 2 3 hashMap.put("A", 100); 4 hashMap.put("B", 120); 5 hashMap.put("C", 105); 6 hashMap.put("D", 110); 7 for(String key: hashMap.keySet()){ 8 System.out.print(key+":"+hashMap.get(key)+" "); 9 }//D:110 A:100 B:120 C:105 10 11 System.out.println(); 12 //将HashMap对象存储到List集合中 13 ArrayList<Map.Entry<String, Integer>> mapList = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet()); 14 //调用Collections的sort方法,并且重写比较器 15 Collections.sort(mapList, new Comparator<Entry<String, Integer>>() { 16 17 @Override 18 public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { 19 // TODO Auto-generated method stub 20 return o1.getValue().compareTo(o2.getValue());//升序 21 // return o2.getValue().compareTo(o1.getValue());//降序 22 } 23 } ); 24 25 for (int i = 0; i < mapList.size(); i++) { 26 System.out.print(mapList.get(i).getKey()+":"+mapList.get(i).getValue()+" "); 27 }//A:100 C:105 D:110 B:120
1 ArrayList<Map.Entry<String, Integer>> mapList = new ArrayList<Map.Entry<String, Integer>>(treeMap.entrySet()); 2 Collections.sort(mapList, new Comparator<Entry<String, Integer>>() { 3 4 @Override 5 public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { 6 // TODO Auto-generated method stub 7 return o1.getValue().compareTo(o2.getValue());//升序 8 // return o2.getValue().compareTo(o1.getValue());//降序 9 } 10 }); 11 for (int i = 0; i < mapList.size(); i++) { 12 System.out.print(mapList.get(i).getKey()+":"+mapList.get(i).getValue()+" "); 13 }//B:100 D:105 F:120 C:300 E:400 A:500 14 15 }
补充:如何实现HashMap按key降序排列?
- 利用Collections类的sort方法实现:将HashMap存储到一个List中,然后调用Collections.sort()方法,同时重写比较器。
- 利用TreeMap的putAll方法实现:创建一个空的TreeMap对象,同时初始化一个降序的比较器,用putAll方法将HashMap中的元素加入TreeMap对象中。
1 Map<String, Integer> hashMap = new HashMap<String, Integer>(); 2 hashMap.put("A", 100); 3 hashMap.put("B", 120); 4 hashMap.put("C", 105); 5 hashMap.put("F", 110); 6 hashMap.put("E", 300); 7 hashMap.put("D", 200); 8 for(String key: hashMap.keySet()){ 9 System.out.print(key+":"+hashMap.get(key)+" "); 10 }//D:200 E:300 F:110 A:100 B:120 C:105 11 12 System.out.println(); 13 14 //调用Collections的sort方法,实现HashMap按key降序排列 15 ArrayList<Map.Entry<String, Integer>> mapList = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet()); 16 Collections.sort(mapList, new Comparator<Entry<String, Integer >>() { 17 18 @Override 19 public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { 20 // TODO Auto-generated method stub 21 return o2.getKey().compareTo(o1.getKey());//降序 22 } 23 }); 24 25 for (Iterator iterator = mapList.iterator(); iterator.hasNext();) { 26 Entry<String, Integer> entry = (Entry<String, Integer>) iterator.next(); 27 System.out.print(entry.getKey()+":"+entry.getValue()+" "); 28 }//F:110 E:300 D:200 C:105 B:120 A:100
1 Map<String, Integer> hashMap = new HashMap<String, Integer>(); 2 hashMap.put("A", 100); 3 hashMap.put("B", 120); 4 hashMap.put("C", 105); 5 hashMap.put("F", 110); 6 hashMap.put("E", 300); 7 hashMap.put("D", 200); 8 for(String key: hashMap.keySet()){ 9 System.out.print(key+":"+hashMap.get(key)+" "); 10 }//D:200 E:300 F:110 A:100 B:120 C:105 11 12 System.out.println(); 13 14 //调用TreeMap的putAll方法,实现HashMap按key降序排列 15 Map<String, Integer> treemap = new TreeMap<String, Integer>(new Comparator<String>() { 16 17 @Override 18 public int compare(String o1, String o2) { 19 // TODO Auto-generated method stub 20 return o2.compareTo(o1);//降序 21 } 22 }); 23 24 treemap.putAll(hashMap); 25 for(String key: treemap.keySet()){ 26 System.out.print(key+":"+treemap.get(key)+" "); 27 }//F:110 E:300 D:200 C:105 B:120 A:100
至此,Map接口的实现类及其用法比较基本介绍完毕。
【推荐】博客园携手 AI 驱动开发工具商 Chat2DB 推出联合终身会员
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何做好技术经理
· Kafka 的“无锁哲学”:高效消息流动的背后
· 时间轮在 Netty , Kafka 中的设计与实现
· MySQL 优化利器 SHOW PROFILE 的实现原理
· 在.NET Core中使用异步多线程高效率的处理大量数据
· 用 Cursor 写出第一个程序
· 如何做好技术经理
· 《HelloGitHub》第 105 期
· 盘点5个常用的.Net依赖注入框架!
· 一句话,我让 AI 帮我做了个 P 图网站!