我将容器类库自己平时编程及看书的感受总结成了三篇博文,前两篇分别是:【Java心得总结六】Java容器中——Collection,第一篇从宏观整体的角度对Java中强大的容器类库做了一个简单总结而第二篇专门针对容器类库中的Collection部分进行了总结。这篇博文将对容器类库中的Map部分进行一个整理总结。
Map:一组成对的“键值对”对象,允许你使用键来查找值。(注:Map其实是将键与值形成的二元组按照一维线性的方式组织起来,这里值得注意的是值可以使一个Collection或者Map,即嵌套结构,如:Map<String,Integer>,Map<Integer,List<String>>,Map<String,Map<String>>(注:所有的基本类型在泛型声明中都必须用其对应的包装类型,关于基本类型及泛型请见博文:【Java心得总结三】Java泛型上——初识泛型)从另一个角度来考虑Map,其实Map相当于ArrayList或者更简单的数组的一种扩展、推广。在数组中我们可以利用下标即数字访问数组当中的不同元素,那么数字与对象之间形成了一种关联,那么如果将这个数字的概念扩展成为对象,那同样的我们可以将对象与对象之间关联起来。即Map,也称为映射表、关联数组、字典允许我们使用一个对象来查找某个对象。
Map可以说是我们平时编程中用到的最多的类库之一了,它说来与数据库中的数据表有些像,但又不同。我们以一个班级成绩存储的例子来做讨论:如果我们想在程序中存储一个班级每个同学的成绩可以用Map<String,Integer>,String部分用来存学生的姓名,Integer用来存学生的成绩;如果我们想存班级每个同学更多的信息,比如不止一门课的成绩,那么我们可以这么用Map<String,List<Integer>>;又如果我们存的每个同学的信息想更完整些比如同学A的语文95、数学98、英语100,那么我们可以这么声明Map<String,Map<String,Integer>>。下面是代码示例:
1 import java.util.ArrayList; 2 import java.util.HashMap; 3 import java.util.List; 4 import java.util.Map; 5 6 public class Main { 7 public static void main(String args[]) { 8 // 首先声明一个Map,录入每个同学的一门课成绩 9 Map<String, Integer> oneGrade = new HashMap<String, Integer>(); 10 oneGrade.put("小明", 98); 11 oneGrade.put("小红", 95); 12 System.out.println("Map<string,Integer>: "); 13 System.out.println(oneGrade); 14 15 // 其次我们想给每个同学录入更多的成绩 16 Map<String, List<Integer>> manyGrades = new HashMap<String, List<Integer>>(); 17 // 录入小明的几科成绩 18 List<Integer> tmp = new ArrayList<Integer>(); 19 tmp.add(98); 20 tmp.add(96); 21 tmp.add(93); 22 manyGrades.put("小明", tmp); 23 // 录入小红的几科成绩 24 tmp.clear(); 25 tmp.add(95); 26 tmp.add(99); 27 tmp.add(100); 28 manyGrades.put("小红", tmp); 29 System.out.println("Map<String,List<Integer>>: "); 30 System.out.println(manyGrades); 31 32 // 再次我们想保留每个同学究竟哪门课的了多少分的具体信息 33 Map<String, Map<String, Integer>> detailGrades = new HashMap<String, Map<String, Integer>>(); 34 //录入小明的具体每门课程的成绩 35 Map<String,Integer> tmp1 = new HashMap<String, Integer>(); 36 tmp1.put("语文", 98); 37 tmp1.put("数学", 96); 38 tmp1.put("英语", 93); 39 detailGrades.put("小明", tmp1); 40 //录入小红的具体每门课的成绩 41 tmp1.clear(); 42 tmp1.put("语文", 95); 43 tmp1.put("数学", 99); 44 tmp1.put("英语", 100); 45 detailGrades.put("小红", tmp1); 46 System.out.println("Map<String,Map<String,Integer>>: "); 47 System.out.println(detailGrades); 48 } 49 }/* Output: 50 Map<string,Integer>: 51 {小明=98, 小红=95} 52 Map<String,List<Integer>>: 53 {小明=[95, 99, 100], 小红=[95, 99, 100]} 54 Map<String,Map<String,Integer>>: 55 {小明={语文=95, 英语=100, 数学=99}, 小红={语文=95, 英语=100, 数学=99}} 56 *///:~
上面的代码非常的简单,值得注意的是
1.如果我们在Map中嵌套了List或者Map那么我们向Map中put数据的时候要先new一个List或者Map(关于List见博文:【Java心得总结六】Java容器中——Collection);
2.我们使用了Map中的库函数clear(),即清空整个Map中的数据,关于Map的API我就不再赘述,官网相当详细;
3.在输出的时候,大家看到我只是直接将Map的引用给了打印函数,可以看到输出的结果会以{key1=value1,key2=value2...}的形式给出;
4.在Map声明时,我们用Map接口持有了HashMap的引用,关于Map接口的不同实现我们将在下面讨论。
二、Map接口的不同实现
先上图,Map的架构图:
Map结构图
——摘自 《Thinking in java》第17章图
从图中我们可以清晰的看到Map接口的具体实现有三个HashMap,TreeMap和LinkedHashMap(Map接口还有WeakHashMap、ConcurrentHashMap、IdentityHashMap等实现,在多线程编程中会用到ConrrentHashMap,另外两个转为特殊问题设计不常用就暂不总结了):
- HashMap:Map基于散列表的实现(它取代了Hashtable)。插入和查询键值对的开销是固定的。可以通过构造器设置容量和负载因子,以调整容器的性能。
- LinkedHashMap:类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点;而在迭代访问时反而更快,因为它使用链表维护内部次序。
- TreeMap:基于红黑树的实现。查看“键”或“键值树”时,它们会被排序(次序由Comparable或Comparator决定)。TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
- ConcurrentHashMap:一种线程安全的Map,它不涉及同步加锁。(以后自己在多线程编程方面有了总结再来讨论)
下面是一个经典的代码示例,展示了这几种不同Map实现的特点:
1 //: containers/Maps.java 2 // Things you can do with Maps. 3 import java.util.concurrent.*; 4 import java.util.*; 5 6 public class Maps { 7 public static void printKeys(Map<Integer, String> map) { 8 System.out.print("Size = " + map.size() + ", "); 9 System.out.print("Keys: "); 10 System.out.println(map.keySet()); // 产生一个键的集合 11 } 12 13 public static void test(Map<Integer, String> map) { 14 System.out.println(map.getClass().getSimpleName()); 15 // 用来作为测试数据 16 Map<Integer, String> testData = new HashMap<Integer, String>(); 17 testData.put(3, "A0"); 18 testData.put(2, "B1"); 19 testData.put(9, "A4"); 20 testData.put(1, "C2"); 21 testData.put(8, "D1"); 22 map.putAll(testData); 23 // Map有Set的特性,keys键值是不能重复的 24 map.putAll(testData); 25 printKeys(map); 26 // 产生值的一个集合 27 System.out.print("Values: "); 28 System.out.println(map.values()); 29 System.out.println(map); 30 System.out.println("map.containsKey(11): " + map.containsKey(11)); 31 System.out.println("map.get(11): " + map.get(11)); 32 System.out.println("map.containsKey(9): " + map.containsKey(9)); 33 System.out.println("map.get(9): " + map.get(9)); 34 System.out.println("map.containsValue(\"F0\"): " 35 + map.containsValue("F0")); 36 Integer key = map.keySet().iterator().next(); 37 System.out.println("First key in map: " + key); 38 map.remove(key); 39 printKeys(map); 40 map.clear(); 41 System.out.println("map.isEmpty(): " + map.isEmpty()); 42 map.putAll(testData); 43 // Operations on the Set change the Map: 44 map.keySet().removeAll(map.keySet()); 45 System.out.println("map.isEmpty(): " + map.isEmpty()); 46 } 47 48 public static void main(String[] args) { 49 test(new HashMap<Integer, String>()); 50 test(new TreeMap<Integer, String>()); 51 test(new LinkedHashMap<Integer, String>()); 52 test(new IdentityHashMap<Integer, String>()); 53 test(new ConcurrentHashMap<Integer, String>()); 54 test(new WeakHashMap<Integer, String>()); 55 } 56 }/* Output: 57 HashMap 58 Size = 5, Keys: [1, 2, 3, 8, 9] 59 Values: [C2, B1, A0, D1, A4] 60 {1=C2, 2=B1, 3=A0, 8=D1, 9=A4} 61 map.containsKey(11): false 62 map.get(11): null 63 map.containsKey(9): true 64 map.get(9): A4 65 map.containsValue("F0"): false 66 First key in map: 1 67 Size = 4, Keys: [2, 3, 8, 9] 68 map.isEmpty(): true 69 map.isEmpty(): true 70 TreeMap 71 Size = 5, Keys: [1, 2, 3, 8, 9] 72 Values: [C2, B1, A0, D1, A4] 73 {1=C2, 2=B1, 3=A0, 8=D1, 9=A4} 74 map.containsKey(11): false 75 map.get(11): null 76 map.containsKey(9): true 77 map.get(9): A4 78 map.containsValue("F0"): false 79 First key in map: 1 80 Size = 4, Keys: [2, 3, 8, 9] 81 map.isEmpty(): true 82 map.isEmpty(): true 83 LinkedHashMap 84 Size = 5, Keys: [1, 2, 3, 8, 9] 85 Values: [C2, B1, A0, D1, A4] 86 {1=C2, 2=B1, 3=A0, 8=D1, 9=A4} 87 map.containsKey(11): false 88 map.get(11): null 89 map.containsKey(9): true 90 map.get(9): A4 91 map.containsValue("F0"): false 92 First key in map: 1 93 Size = 4, Keys: [2, 3, 8, 9] 94 map.isEmpty(): true 95 map.isEmpty(): true 96 IdentityHashMap 97 Size = 5, Keys: [2, 9, 8, 3, 1] 98 Values: [B1, A4, D1, A0, C2] 99 {2=B1, 9=A4, 8=D1, 3=A0, 1=C2} 100 map.containsKey(11): false 101 map.get(11): null 102 map.containsKey(9): true 103 map.get(9): A4 104 map.containsValue("F0"): false 105 First key in map: 2 106 Size = 4, Keys: [9, 8, 3, 1] 107 map.isEmpty(): true 108 map.isEmpty(): true 109 ConcurrentHashMap 110 Size = 5, Keys: [8, 2, 9, 1, 3] 111 Values: [D1, B1, A4, C2, A0] 112 {8=D1, 2=B1, 9=A4, 1=C2, 3=A0} 113 map.containsKey(11): false 114 map.get(11): null 115 map.containsKey(9): true 116 map.get(9): A4 117 map.containsValue("F0"): false 118 First key in map: 8 119 Size = 4, Keys: [2, 9, 1, 3] 120 map.isEmpty(): true 121 map.isEmpty(): true 122 WeakHashMap 123 Size = 5, Keys: [9, 8, 3, 2, 1] 124 Values: [A4, D1, A0, B1, C2] 125 {9=A4, 8=D1, 3=A0, 2=B1, 1=C2} 126 map.containsKey(11): false 127 map.get(11): null 128 map.containsKey(9): true 129 map.get(9): A4 130 map.containsValue("F0"): false 131 First key in map: 9 132 Size = 4, Keys: [8, 3, 2, 1] 133 map.isEmpty(): true 134 map.isEmpty(): true 135 *///:~