前言:
关于Map.merge()这可能是Key-Value中最通用的操作。但它也相当模糊很少使用。
merge()
:它将新值置于给定键下(如果不存在)或更新具有给定值的现有键(UPSERT)。
示例 :
//计算唯一的单词出现次数
var map = new HashMap<String, Integer>();
words.forEach(word -> {
var prev = map.get(word);
if (prev == null) {
map.put(word, 1);
} else {
map.put(word, prev + 1);
}
});
复制代码
//给定输入 对应的输出结果
var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz");
//...
{Bar=1, Fizz=2, Foo=3, Buzz=2}复制代码
//进行重构以避免条件逻辑
words.forEach(word -> {
map.putIfAbsent(word, 0);
map.put(word, map.get(word) + 1);
});复制代码
putIfAbsent()
map.get(word)
里面
map.put()
有点尴尬。优化
words.forEach(word -> {
map.putIfAbsent(word, 0);
map.computeIfPresent(word, (w, prev) -> prev + 1);
});复制代码
computeIfPresent()
word
)中的键存在时才调用给定的转换。否则它什么都不做。
words.forEach(word ->
map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);复制代码
compute()
computeIfPresent()
无论给定key是否存在都会调用它。如果键的值不存在,则
prev
参数为
null。故实现一个简单的merge:
default V merge(K key, V value, BiFunction<V, V, V> remappingFunction) {
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if (newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}复制代码
merge()
适用于两种情况。如果给定的Key值不存在,它就变成了put(key, value)
。但如果所述Key已经存在一些值,
remappingFunction
- 只需返回新值覆盖旧值:
(old, new) -> new
- 只需返回旧值保留旧值:
(old, new) -> old
- 以某种方式合并两者,例如:
(old, new) -> old + new
- 甚至删除旧值:
(old, new) -> null
//1 的下word ,如果没有key就将1添加到现有值。
words.forEach(word ->
map.merge(word, 1, (prev, one) -> prev + one)
);复制代码
使用merge()模拟一个帐户操作
//vo
class Operation {
private final String accNo;
private final BigDecimal amount;
}
//针对不同账户的操作
var operations = List.of(
new Operation("123", new BigDecimal("10")),
new Operation("456", new BigDecimal("1200")),
new Operation("123", new BigDecimal("-4")),
new Operation("123", new BigDecimal("8")),
new Operation("456", new BigDecimal("800")),
new Operation("456", new BigDecimal("-1500")),
new Operation("123", new BigDecimal("2")),
new Operation("123", new BigDecimal("-6.5")),
new Operation("456", new BigDecimal("-600"))
);复制代码
如果想要为每个账户计算余额,不使用merge:
var balances = new HashMap<String, BigDecimal>();
operations.forEach(op -> {
var key = op.getAccNo();
balances.putIfAbsent(key, BigDecimal.ZERO);
balances.computeIfPresent(key, (accNo, prev) -> prev.add(op.getAmount()));
});复制代码
使用merge():
operations.forEach(op ->
balances.merge(op.getAccNo(), op.getAmount(),
(soFar, amount) -> soFar.add(amount))
);
operations.forEach(op ->
balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add)
);
//{123=9.5, 456=-100}复制代码