Java 集合之 ​Map

巴英韶
2023-12-01

目录

1、Map 的基础操作

2、批量操作 Bulk Operations

3、集合视图 Collection Views

4、Map代数 Map Algebra


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)))

1、Map 的基础操作

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());
        }
    }
}

2、批量操作 Bulk Operations

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;
}

3、集合视图 Collection Views

 Collection Views 允许通过以下三种方式将Map视为集合:

  • keySet — Map中包含的键的集合(set 集合)。
  • values — Map中包含的值的集合。此集合不是Set,因为多个键可以映射到相同的值。
  • entrySet — Map中包含的键值对的集合。在Map接口中提供了一个嵌套接口 Map.EntryentrySet 集合中的元素类型就是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<KeyType, ValType> e : m.entrySet())
    System.out.println(e.getKey() + ": " + e.getValue());

4、Map代数 Map Algebra

在使用集合时,批量操作 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

 类似资料: