This article brings you a detailed introduction to Map.merge() (with code). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
Today we introduce the merge method of Map, let us take a look at its power.
In the JDK API, such a method is very special, it is very novel, and it is worth our time to understand. It is also recommended that you can apply it to actual project code. You guys should be of great help. Map.merge()). This is probably the most versatile operation on Map. But it's also quite obscure, and few people use it.
Background introduction
merge() can be explained as follows: it assigns a new value to the key (if it does not exist) or updates an existing key with a given value (UPSERT). Let's start with the most basic example: counting unique word occurrences. Before Java 8, the code was very confusing, and the actual implementation had actually lost its essential design meaning.
var map = new HashMap<string>(); words.forEach(word -> { var prev = map.get(word); if (prev == null) { map.put(word, 1); } else { map.put(word, prev + 1); } });</string>
According to the logic of the above code, assuming an input set is given, the output result is as follows;
var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz"); //... {Bar=1, Fizz=2, Foo=3, Buzz=2}
Improve V1
Now let us reconstruct it, mainly removing Some of its judgment logic;
words.forEach(word -> { map.putIfAbsent(word, 0); map.put(word, map.get(word) + 1); });
Such improvements can meet our reconstruction requirements. The specific usage of putIfAbsent() will not be described in detail. The line of code putIfAbsent is definitely needed, otherwise, the subsequent logic will report an error. In the code below, it is strange that put and get appear again. Let us continue to improve the design.
Improve V2
words.forEach(word -> { map.putIfAbsent(word, 0); map.computeIfPresent(word, (w, prev) -> prev + 1); });
computeIfPresent is to call the given conversion only when the key in the word exists. Otherwise it handles nothing. We ensure the key exists by initializing it to zero, so the increment is always valid. Is this implementation perfect enough? Not necessarily, there are other ideas to reduce additional initialization.
words.forEach(word -> map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1) );
compute () is like computeIfPresent(), but it is called regardless of the presence or absence of the given key. If the value of key does not exist, the prev parameter is null. Moving a simple if to a ternary expression hidden in a lambda is also far from optimal performance. Before I show you the final version, let's take a look at a slightly simplified source code analysis of the default implementation of Map.merge().
Improve V3
merge() source code
default V merge(K key, V value, BiFunction<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; }</v>
Code snippets are worth a thousand words. You can always discover new lands by reading the source code. merge() is suitable for both situations. If the given key does not exist, it becomes put(key, value). However, if the key already has some values, our remappingFunction can choose to merge. This feature is perfect for the above scenario:
- Just return the new value to overwrite the old value:
(old, new) -> new
- Just return the old value to keep the old value:
(old, new) -> old
- merge the two in some way, like:
(old, new) -> old new
- Even delete the old value:
(old, new) -> null
As you can see, it merge () is very general. So, our question is how to use merge()? The code is as follows:
words.forEach(word -> map.merge(word, 1, (prev, one) -> prev + one) );
You can understand it as follows: if there is no key, then the initialized value is equal to 1; otherwise, 1 is added to the existing value. One in the code is a constant, because in our scenario, the default is always plus 1, and the specific changes can be switched at will.
Scenario
Imagine, is merge() really that easy to use? What can its scenes be?
As an example. You have an account operation class
class Operation { private final String accNo; private final BigDecimal amount; }
and a series of operations for different accounts:
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")) );
We want to calculate the balance (total operating amount) for each account. If merge() is not used, it becomes very troublesome:
Map balances = new HashMap<string>(); operations.forEach(op -> { var key = op.getAccNo(); balances.putIfAbsent(key, BigDecimal.ZERO); balances.computeIfPresent(key, (accNo, prev) -> prev.add(op.getAmount())); });</string>
Use the code after merge
operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), (soFar, amount) -> soFar.add(amount)) );
The logic of optimization.
operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add) );
Of course the result is correct, is such a concise code exciting? For each operation, add
is given accNo
at the given amount
.
{ 123 = 9.5,456 = - 100 }
ConcurrentHashMap
When we extend to ConcurrentHashMap, when Map.merge appears, the combination with ConcurrentHashMap is very perfect. This matching scenario is for single-thread-safe logic that automatically performs insert or update operations.
This article has ended here. For more other exciting content, you can pay attention to the Java Tutorial Video column of the PHP Chinese website!
The above is the detailed content of Detailed introduction of Map.merge() (with code). For more information, please follow other related articles on the PHP Chinese website!

JVMmanagesgarbagecollectionacrossplatformseffectivelybyusingagenerationalapproachandadaptingtoOSandhardwaredifferences.ItemploysvariouscollectorslikeSerial,Parallel,CMS,andG1,eachsuitedfordifferentscenarios.Performancecanbetunedwithflagslike-XX:NewRa

Java code can run on different operating systems without modification, because Java's "write once, run everywhere" philosophy is implemented by Java virtual machine (JVM). As the intermediary between the compiled Java bytecode and the operating system, the JVM translates the bytecode into specific machine instructions to ensure that the program can run independently on any platform with JVM installed.

The compilation and execution of Java programs achieve platform independence through bytecode and JVM. 1) Write Java source code and compile it into bytecode. 2) Use JVM to execute bytecode on any platform to ensure the code runs across platforms.

Java performance is closely related to hardware architecture, and understanding this relationship can significantly improve programming capabilities. 1) The JVM converts Java bytecode into machine instructions through JIT compilation, which is affected by the CPU architecture. 2) Memory management and garbage collection are affected by RAM and memory bus speed. 3) Cache and branch prediction optimize Java code execution. 4) Multi-threading and parallel processing improve performance on multi-core systems.

Using native libraries will destroy Java's platform independence, because these libraries need to be compiled separately for each operating system. 1) The native library interacts with Java through JNI, providing functions that cannot be directly implemented by Java. 2) Using native libraries increases project complexity and requires managing library files for different platforms. 3) Although native libraries can improve performance, they should be used with caution and conducted cross-platform testing.

JVM handles operating system API differences through JavaNativeInterface (JNI) and Java standard library: 1. JNI allows Java code to call local code and directly interact with the operating system API. 2. The Java standard library provides a unified API, which is internally mapped to different operating system APIs to ensure that the code runs across platforms.

modularitydoesnotdirectlyaffectJava'splatformindependence.Java'splatformindependenceismaintainedbytheJVM,butmodularityinfluencesapplicationstructureandmanagement,indirectlyimpactingplatformindependence.1)Deploymentanddistributionbecomemoreefficientwi

BytecodeinJavaistheintermediaterepresentationthatenablesplatformindependence.1)Javacodeiscompiledintobytecodestoredin.classfiles.2)TheJVMinterpretsorcompilesthisbytecodeintomachinecodeatruntime,allowingthesamebytecodetorunonanydevicewithaJVM,thusfulf


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

Atom editor mac version download
The most popular open source editor

VSCode Windows 64-bit Download
A free and powerful IDE editor launched by Microsoft

Notepad++7.3.1
Easy-to-use and free code editor

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function
