Java8给这门语言带来了函数式编程的春风,使用lambda算子和stream流,让coding操作大道至简。
Map接口是哈希表的常用接口,也是集合框架中重要的组成部分,在对key和value进行增删改查时,往往需要不断判断if和else的嵌套编码操作,并使用contains()和put(),putIfAbsent(),putIfPresent(),remove()等内置函数,使得代码冗余且复杂。
丑陋的新增与删除#
例如,有以下丑陋臃肿的代码:
if (!map.containsKey(key)) { map.put(key, 1);} else { map.put(key, map.get(key) + 1);}这种操作经常出现在对新增一个key时候,key不存在则需要调用put,存在时则先拿到value,再put。
第二个样例:
if (map.containsKey(key)) { map.put(key, map.get(key) - 1); if (map.get(key) == 0) { map.remove(key); }}这种操作也很常见,出现在对key对应的value进行扣减1的操作,如果value扣减到0就删除这个key,也是频繁调用containsKey(),put(),get()等函数,代码异常臃肿。
简化操作#
Java 8 的 merge 和 compute 的出现,直接让这种重复的模板代码消失了。
merge() - 旧值替换新值#
V merge(K key, V value, BiFunction<V, V, V> remappingFunction)merge()方法接受key,默认值value,以及value存在时候的remappingFunction,它是一个BiFunction类型接口,接受三个范型参数T,U,R,表示两个输入类型和一个输出类型。
merge()操作如下:
- key 不存在 → 插入 value(newValue)
- key 存在 → (oldValue, newValue) 合并成新 value
- 如果remappingFunction返回 null → 删除 key
因此上述新增操作可以简化为:
map.merge(c, 1, Integer::sum);表示若c对应key不存在则插入(key,1),否则将插入(key,value+1),实现了丑陋代码的改进。
compute() - 可定制统一更新入口#
compute()一般用在需要复杂逻辑的处理地方,比如根据某些条件删除key或者更新特定的value值。
V compute(K key, BiFunction<K, V, V> remappingFunction)可以看到,compute()函数的入参就比merge()少了一个value,这并不是说没有默认值,而是将这个默认值直接使用remappingFunction的返回值进行替换。
例如,remappingFunction返回一个1,那就将value设置为1并put,如果返回null则将这个entry删除。
所以,之前的删除代码可以简化为:
map.compute(c, (k, v) -> (v == null || v == 1) ? null : v - 1);注意这里的判空逻辑,需要格外判断v==null是否这个entry不存在,不然会走v-1引起NPE。代码语义是如果value存在则减1,减到0就删除。因此使用compute()时需要更加谨慎一些。
总结#
如果能够熟练使用以上两个函数式编程的方法,相信会对哈希操作得心应手。