Suwalszczyzna

merge()

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

prev

null

if

merge()

Map.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()

put(key, value)

remappingFunction

overwrite old value by simply returning the new one: (old, new) -> new

keep the old value by simply returning the old one: (old, new) -> old

somehow merge the two, e.g.: (old, new) -> old + new

or even remove old value: (old, new) -> null

merge()

merge()

words.forEach(word -> map.merge(word, 1, (prev, one) -> prev + one) );

1

word

1

one

remappingFunction

(word, 1, prev -> prev + 1)

merge()

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

add

amount

accNo

{123=9.5, 456=-100}

ConcurrentHashMap

Map.merge()

ConcurrentHashMap

ConcurrentHashMap

get()

put()

merge()

Russian translation available: Map.merge () - метод, чтобы управлять всеми остальными I don’t often explain a single method in JDK, but when I do, it’s about. Probably the most versatile operation in the key-value universe. And also rather obscure and rarely used.can be explained as follows: it either puts new value under the given key (if absent) or updates existing key with a given value (UPSERT). Let’s start with the most basic example: counting unique word occurrences. Pre-Java 8 (read: pre-2014!) code was quite messy and the essence was lost in implementation details:However, it works and for given input produces desired output:OK, but let’s try to refactor it to avoid conditional logic:That’s nice!is a necessary evil, otherwise, the code breaks on the first occurrence of a previously unknown word. Also, I findinsidea bit awkward. Let’s get rid of it as well!invokes given transformation only if key in question () exists. Otherwise does nothing. We made sure key exists, by initializing it to zero, so incrementation always works. Can we do better? We can cut the extra initialization, but I wouldn’t recommend it:is like, but invoked irrespective to the existence of given key. If value for the key does not exist,argument is. Moving simpleto ternary expression hidden in lambda is far from optimal. This is whereoperator shines. Before I’ll show you the final version, let’s see a slightly simplified default implementation ofThe code snippet is worth a thousand words.works in two scenarios. If the given key is not present, it simply becomes. However, if said key already holds some value, ourmay merge (duh!) the old and the one. This function is free to:As you can seeis quite versatile. So how does our academic problem look like with? It’s quite pleasing:You can read it as follows: putunderkey if absent, otherwise addto existing value. I named one of the parameters “” because in our example it’s always… 1. Sadlytakes two parameters, where the second one is the value we are about to upsert (insert or update). Technically we know this value already, sowould be much easier to digest. But there’s no such API.All right, but isreally useful? Imagine you have an account operation (constructor, getters and other useful properties omitted):And a bunch of operations for different accounts:We would like to compute balance (total over operations’ amounts) for each account. Withoutthis is quite cumbersome:But with a little help ofDo you see a method reference opportunity here?I find this astoundingly readable. For each operationgivento given. The results are as expected:shines even brighter when you realize it’s properly implemented in. This means we can atomically perform insert-or-update operation. Single line and thread-safe.is obviously thread-safe, but not across many operations, e.g.and then. Howevermakes sure no updates are lost.