目录
Map是一个将键映射到值的对象。map不能包含重复的键,它模拟数学函数的抽象。
Java平台包含三种通用Map实现:HashMap、TreeMap和LinkedHashMap。它们的行为和性能完全类似于HashSet、TreeSet和LinkedHashSet。
例如,Map常见的应用场景,按部门对员工进行分组
- // Group employees by department
- Map<Department, List<Employee>> byDept = employees.stream()
- .collect(Collectors.groupingBy(Employee::getDepartment));
或者按部门计算工资总和
- // Compute sum of salaries by department
- Map<Department, Integer> totalByDept = employees.stream()
- .collect(Collectors.groupingBy(Employee::getDepartment,
- Collectors.summingInt(Employee::getSalary)));
或者通过及格或不及格来分组学生:
- // Partition students into passing and failing
- Map<Boolean, List<Student>> passingFailing = students.stream()
- .collect(Collectors.partitioningBy(s -> s.getGrade()>= PASS_THRESHOLD));
甚至同时使用多个条件进行分类:
- // Cascade Collectors
- Map<String, Map<String, List<Person>>> peopleByStateAndCity
- = personStream.collect(Collectors.groupingBy(Person::getState,
- Collectors.groupingBy(Person::getCity)))
Map的基本操作(put、get、containsKey、containsValue、size和isEmpty)与Hashtable中的对应操作完全相同。
下面的程序统计字符串在参数列表中出现的次数。
- public class Freq {
- public static void main(String[] args) {
- args = "java freq if it is to be it is up to me to delegate".split(" ");
- Map<String, Integer> m = new TreeMap<>();
- // Initialize frequency table from command line
- for (String a : args) {
- Integer freq = m.get(a);
- m.put(a, (freq == null) ? 1 : freq + 1);
- }
- System.out.println(m.size() + " distinct words:");
- System.out.println(m);
- }
- }
该程序产生以下输出:
10 distinct words:
{delegate=1, java=1, be=1, freq=1, me=1, is=2, it=2, to=3, up=1, if=1}
如果希望看到按字母顺序排列的结果,可以将Map的实现类型从HashMap更改为TreeMap。同一命令行会生成以下输出:
10 distinct words:
{be=1, delegate=1, freq=1, if=1, is=2, it=2, java=1, me=1, to=3, up=1}
同样,将映射的实现类型更改为LinkedHashMap,就可以使程序按照单词在命令行中首次出现的顺序进行打印。同一命令行会产生以下输出:
10 distinct words:
{java=1, freq=1, if=1, it=2, is=2, to=3, be=1, up=1, me=1, delegate=1}
Map 提供的构造函数支持Map对象作为入参:
- // 其中入参m,为另一个map
- Map<K, V> copy = new HashMap<K, V>(m);
简单的示例程序
- import java.util.HashMap;
- import java.util.Map;
-
- public class Freq {
- public static void main(String[] args) {
- Map<String, String> m = new HashMap<>();
- m.put("A", "a");
- m.put("B", "b");
- m.put("C", "c");
- Map<String, String> copy = new HashMap<>(m);
- for (Map.Entry<String, String> entry : copy.entrySet()) {
- System.out.println(entry.getKey() + ":" + entry.getValue());
- }
- }
- }
clear 从Map中删除所有映射。
putAll 操作Map是对Collection接口addAll操作的模拟。putAll 操作与Map有参构造函数结合使用,为创建带默认值的map提供了一种简洁的方法。下边是演示代码:
- static <K, V> Map<K, V> newAttributeMap(Map<K, V>defaults, Map<K, V> overrides) {
- Map<K, V> result = new HashMap<K, V>(defaults);
- result.putAll(overrides);
- return result;
- }
Collection Views 允许通过以下三种方式将Map视为集合:
keySet — Map中包含的键的集合(set 集合)。values — Map中包含的值的集合。此集合不是Set,因为多个键可以映射到相同的值。entrySet — Map中包含的键值对的集合。在Map接口中提供了一个嵌套接口 Map.Entry,entrySet 集合中的元素类型就是Entry(键值对)。Collection Views 提供了在Map上迭代的方法。下边例子演示了使用for-each在Map中遍历键的标准用法:
- for (KeyType key : m.keySet())
- System.out.println(key);
使用迭代器
- // Filter a map based on some
- // property of its keys.
- for (Iterator<Type> it = m.keySet().iterator(); it.hasNext(); )
- if (it.next().isBogus())
- it.remove();
遍历键-值对的标准用法:
- for (Map.Entry
e : m.entrySet()) - System.out.println(e.getKey() + ": " + e.getValue());
在使用集合时,批量操作 containsAll、removeAll和retainAll等,都是非常有效的工具。
在Map中如何实现这一操作?比如,想知道Map a是否包含Map b中的所有键值映射。下边代码实现了这一功能
- if (m1.entrySet().containsAll(m2.entrySet())) {
- ...
- }
同样的,如果想知道两个Map对象是否包含相同键的映射。
- if (m1.keySet().equals(m2.keySet())) {
- ...
- }
应用场景:如果有一个表示属性-值对的集合的Map,以及两个表示所需属性和允许属性的set。下面的代码将确定属性映射是否符合这些约束,如果不符合,则打印详细的错误消息。
- static <K, V> boolean validate(Map<K, V> attrMap, Set<K> requiredAttrs, Set<K> permittedAttrs) {
- boolean valid = true;
- Set<K> attrs = attrMap.keySet();
- if (!attrs.containsAll(requiredAttrs)) { // 检验是否符合约束
- Set<K> missing = new HashSet<K>(requiredAttrs); // copy原Map
- missing.removeAll(attrs); // 移除掉已经存在的约束
- System.out.println("Missing attributes: " + missing); // 打印没有匹配到的约束
- valid = false; // 校验失败
- }
- if (!permittedAttrs.containsAll(attrs)) {
- Set<K> illegal = new HashSet<K>(attrs);
- illegal.removeAll(permittedAttrs);
- System.out.println("Illegal attributes: " + illegal);
- valid = false;
- }
- return valid;
- }
如果想知道两个Map对象共有的所有键
- Set<KeyType>commonKeys = new HashSet<KeyType>(m1.keySet());
- commonKeys.retainAll(m2.keySet()); // 交集
如果想删除一个Map与另一个Map共有的键值对或者键。
- m1.entrySet().removeAll(m2.entrySet()); // 删除键值对
- m1.keySet().removeAll(m2.keySet()); // 删除键
参考文章:https://docs.oracle.com/javase/tutorial/collections/interfaces/map.html