Map简介

前言

Collection家族的最后一个重要成员就是Map了,坦白来说,在实际开发中,用到的最多的怕就是List和Map了,而相对来说Set比他们用的都少一点。Map的K/V存储结构,特别适合在后端开发中存放一些用户请求参数,配置信息等。因此,准备详细分析一下Map家族,大致分为三个部分Map、HashMap、TreeMap。

Map介绍

Map是一个接口,它表示了一组可以存放K/V键值对的集合。key值必须是唯一的,不能重复,对应的value值同一时刻只能指向一个对象。Map的定义是使用了泛型的,其定义语法为 public interface Map<K,V>, K为键的类型,V为value的类型。可以是通用数据类型也可以是类。

Map提供了三种视图可以用于遍历,key值得集合,value值得集合,k/v对的集合。有一些子类的遍历顺序是一定的,如TreeMap,而另一些是不确定的,如HashMap。Map主要提供了以下的方法:

  • 查询类的:size返回当前大小、isEmpty返回是否为空,containsKey、containsValue分别返回是否包含该key或value,
  • 操作类的:get(key)、put(key, value)、remove(key)、putAll(map)、clear()
  • 视图类的:keySet()返回key值的一个set集合对象,values返回一个Collection对象,entrySet返回一个Set> 对象,Map.Entry是一个内部静态类

根据视图类的三个方法,分别有三种遍历方法

1
2
3
4
5
6
7
8
9
10
11
12
13
1/遍历k,v值
for(K key: map.keySet()) {
V value = map.get(key);
}
2/只遍历v值
for(V value: map.values()) {
}
3/遍历k,v值
for(Map.Entry<K,V> entry: map.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
}

JDK1.8之后,接口中的方法可以提供默认的结构体了,叫作接口的默认方法,该方法提供了默认操作,如果实现类没有重写这些方法,则将直接调用默认方法的逻辑,这赋予了接口部分父类的功能,免除了实现类重复的实现相同的方法逻辑。Map中的默认方法大多与1.8支持的lambda表达式有关,通过传入Function,提供了函数式编程的部分特性。

默认方法中有一部分是无关Lambda表达式的,这部分方法的意义就是纯粹的提供了一个默认的操作逻辑。这些方法包含了

  • putIfAbsent(K key, V value),该方法判断key对应的是否为null,如果是,则赋值。
  • remove(Object key, Object value)删除指定key/value对,这里使用了Object而没有采用泛型,具体原因没有分析出来。
  • replace(K key, V oldValue, V newValue)将旧值替换为新值
  • replace(K key, V value)将该key对应的value设为传入的value。

默认方法中另一组方法是传入参数为Function或者BiFunction的,Function和BiFunction为依赖注入的对象,在默认方法调用其apply方法,对Map的元素进行相应的操作。依赖注入是spring中重要的一个思想,它通过注入一个接口,达到了控制反转的目的。在这里,默认方法都调用apply方法,但apply方法的具体实现逻辑在这里并没有指定,而是通过传入的Function或者BiFunction的实现类来决定的,也就是把具体控制权交给了其实现类。简单介绍一下两个接口。

  • 两者都可以用Lambda表达式传入
  • Function的apply方法传入一个参数,BiFunction的apply方法传入两个参数。
  • 两者的apply方法都会返回一个值

computeIfAbsent传入一个key值和Function,在调用时可以使用 map.computeIfAbsent(1, k -> k+"")的格式,将key为1(不存在与Map中)对应的value设置为字符串“1”。

compute、computeIfPresent、merge、replaceAll传入的都为BiFunction。

compute的典型用法如
map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg)) 将key对应的value设一个默认字符串或者将原有字符串和新字符串合并。merge的用法与compute类似。map.merge(key, msg, String::concat)

computeIfPresent对一个存在的key值进行对应的apply方法调用。replaceAll对所有的k/v对进行相应的操作。

默认方法还有一个传入的是BiConsumer接口。该接口调用的是accept方法,传入两个参数,不返回值。该默认方法为forEach方法,可以方便的用于遍历map.如 map.forEach((k, v) -> System.out.println(k +"->"+ v));

小结

Map是Collection框架中的大家族,其中有HashMap、HashTable、TreeMap都有分析的必要,因此决定单独几篇分析每个类。在这里,简单介绍了Map的功能和特性,重点介绍了1.8后支持的默认方法,给接口的使用增加了很多的便利性。灵活使用默认方法搭配Lambda表达式,可以写出来简洁且清晰的代码。