本日引见Map的merge要领,让我们来看看它的壮大的地方。
在JDK的API中,如许的一个要领它是很迥殊的,它很新鲜,它是值得我们花时间去相识的,同时也引荐你可以运用到现实的项目代码中,对你们应当协助很大。Map.merge())。这多是Map中最通用的操纵。但它也相称隐约,险些很少人会去运用它。
背景引见
merge() 可以诠释以下:它将新的值赋值给到key中(如果不存在)或更新具有给定值的现有key(UPSERT)。让我们从最基本的例子最先:盘算唯一的单词涌现次数。在java8之前的时刻,代码异常杂沓,现实的完成实在已失去了实质层面的设想意义。
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}
革新V1
如今让我们来重构它,重要去掉它的一些推断逻辑;
words.forEach(word -> { map.putIfAbsent(word, 0); map.put(word, map.get(word) + 1); });
如许的革新,是可以满足我们的重构请求。putIfAbsent()的详细用法就不过量形貌。putIfAbsent那一行代码是肯定须要的,不然,背面的逻辑也就会报错。而在下面代码中,又涌现了put、get这一点会很新鲜,让我们再继承的举行革新设想。
革新V2
words.forEach(word -> { map.putIfAbsent(word, 0); map.computeIfPresent(word, (w, prev) -> prev + 1); });
computeIfPresent是仅当 word中的的key存在的时刻才挪用给定的转换。不然它什么都不处置惩罚。我们经由过程将key初始化为零来确保key存在,因而增量一直有用。如许的完成是否是已充足圆满?未必,另有其他的思绪可以削减分外的初始化。
words.forEach(word -> map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1) );
compute ()就像是computeIfPresent(),但不管给定key的存在与否怎样都邑挪用它。如果key的值不存在,则prev参数为null。将简朴挪动if 到隐藏在lambda中的三元表达式也远远没有到达最好的表现。在我向你展现终究版本之前,让我们看一下轻微简化的默许完成Map.merge()源码剖析。
革新V3
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
如你所见,它 merge() 是异常通用的。那末,我们的题目该怎样运用merge()呢?代码以下:
words.forEach(word -> map.merge(word, 1, (prev, one) -> prev + one) );
你可以根据以下思绪明白:如果没有key,那末初始化的value即是1;不然,将1添加到现有值。代码中的 one 是一个常量,由于我们的场景中,默许一直是加1,详细变化可以随便切换。
场景
设想一下,merge()真的那末好用吗?它的场景可以有什么?
举一个例子。你有一个帐户操纵类
class Operation { private final String accNo; private final BigDecimal amount; }
以及针对差别帐户的一系列操纵:
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(),就变得异常麻烦了:
Map 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) );
固然效果是准确的,如许简约的代码心动吗?关于每一个操纵,add
在给定的amount
给定accNo
。
{ 123 = 9.5,456 = - 100 }
ConcurrentHashMap
当我们再延伸到ConcurrentHashMap来,当 Map.merge的涌现,和ConcurrentHashMap的连系那是异常的圆满的。如许的搭配场景是关于那些自动实行插进去或许更新操纵的单线程平安的逻辑。
本篇文章到这里就已悉数完毕了,更多其他精彩内容可以关注ki4网的Java教程视频栏目!
以上就是Map.merge()的细致引见(附代码)的细致内容,更多请关注ki4网别的相干文章!