eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this tutorial, we’ll look at several ways to increment a numerical value associated with a key in a Map. The Maps interface, part of the Collections framework in Java, represents a collection of key-value pairs. Some common Map implementations include the HashMap, TreeMap, and LinkedHashMap.

2. Problem Statement

Let’s see an example where we have a sentence as a String input and store the frequencies of each character that appears in the sentence in a Map. Here’s an example of the problem and the output:

sample sentence:
"the quick brown fox jumps over the lazy dog"

character frequency:

t: 2 times
h: 2 times
e: 3 times
q: 1 time
u: 2 times
...... and so on

The solution will involve storing the character frequencies in a Map where the key is a character, and the value is the total number of times the character appears in the given String. As there can be repeated characters in a String, we’ll need to update the value associated with a key, or rather increment the current value of a key many times.

3. Solutions

3.1. Using containsKey()

The simplest solution to our problem is to walk through the String, and at each step, we check the character’s existence in the map using the containsKey() method. If the key exists, we increment the value by 1 or put a new entry in the map with the key as the character and the value as 1:

public Map<Character, Integer> charFrequencyUsingContainsKey(String sentence) {
    Map<Character, Integer> charMap = new HashMap<>();
    for (int c = 0; c < sentence.length(); c++) {
        int count = 0;
        if (charMap.containsKey(sentence.charAt(c))) {
            count = charMap.get(sentence.charAt(c));
        }
        charMap.put(sentence.charAt(c), count + 1);
    }
    return charMap;
}

3.2. Using getOrDefault()

Java 8 introduced the getOrDefault() method in Map as a simple way to retrieve the value associated with a key in a Map or a default preset value if the key doesn’t exist:

V getOrDefault(Object key, V defaultValue);

We’ll use this method to fetch the value associated with the current character of the String (our key) and increment the value by 1. This is a simpler and less verbose alternative to containsKey():

public Map<Character, Integer> charFrequencyUsingGetOrDefault(String sentence) {
    Map<Character, Integer> charMap = new HashMap<>();
    for (int c = 0; c < sentence.length(); c++) {
        charMap.put(sentence.charAt(c),
          charMap.getOrDefault(sentence.charAt(c), 0) + 1);
    }
    return charMap;
}

3.3. Using merge()

The merge() method is provided by Java 8 as a way to override the values associated with a specific key with an updated value in a Map. The method takes in a key, a value, and a remapping function, which is used to compute the new updated value that will replace the existing value in the Map:

default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

The remapping function is a BiFunction, which means that it follows the functional programming paradigm of Java 8. We send our desired function inline as an argument to the merge() method along with the parameters, and it performs the desired function.

The method expects us to define a default value, which will be merged with the result of the remapping function. Hence, we can write our remapping function as a summation of the default value(which is 1) and the current value that exists for the key:

(a, b) -> a + b

We can also rewrite the same using method reference:

public Map<Character, Integer> charFrequencyUsingMerge(String sentence) {
    Map<Character, Integer> charMap = new HashMap<>();
    for (int c = 0; c < sentence.length(); c++) {
        charMap.merge(sentence.charAt(c), 1, Integer::sum);
    }
    return charMap;
}

3.4. Using compute()

The compute() method differs from the merge() method in that the compute() method has no effect on missing keys and does not throw an exception. The method does not take any value, and the remapping function is supposed to decide the new value:

V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

We’ll need to handle the case when the key is missing and the value is null and set the value to the default 1.

public Map<Character, Integer> charFrequencyUsingCompute(String sentence) {
    Map<Character, Integer> charMap = new HashMap<>();
    for (int c = 0; c < sentence.length(); c++) {
        charMap.compute(sentence.charAt(c), (key, value) -> (value == null) ? 1 : value + 1);
    }
    return charMap;
}

3.5. Using incrementAndGet() of AtomicInteger

We can use the AtomicInteger type to store the frequencies of the characters instead of our regular Integer wrapper class. This benefits us by introducing atomicity to the code, and we can use the incrementAndGet() method of the AtomicInteger class. This method performs an atomic increment operation on the value, equivalent to a ++i operation.

Additionally, we should make sure that the key exists in place using the putIfAbsent() method of Map:

public Map<Character, AtomicInteger> charFrequencyWithGetAndIncrement(String sentence) {
    Map<Character, AtomicInteger> charMap = new HashMap<>();
    for (int c = 0; c < sentence.length(); c++) {
        charMap.putIfAbsent(sentence.charAt(c), new AtomicInteger(0));
        charMap.get(sentence.charAt(c)).incrementAndGet();
    }
    return charMap;
}

Notice that the return type of this method uses AtomicInteger.

Furthermore, we can rewrite the same code in a slightly more compact way by using computeIfAbsent() by passing a remapping function to it:

public Map<Character, AtomicInteger> charFrequencyWithGetAndIncrementComputeIfAbsent(String sentence) {
    Map<Character, AtomicInteger> charMap = new HashMap<>();
    for (int c = 0; c < sentence.length(); c++) {
        charMap.computeIfAbsent(sentence.charAt(c), k-> new AtomicInteger(0)).incrementAndGet();
    }
    return charMap;
}

3.6. Using Guava

We can use the Guava library to solve the problem as well. To use Guava, we should first add the Maven library as a dependency:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

Guava provides an AtomicLongMap class, which has an inbuilt getAndIncrement() method which helps us in our use case:

public Map<Character, Long> charFrequencyUsingAtomicMap(String sentence) {
    AtomicLongMap<Character> map = AtomicLongMap.create();
    for (int c = 0; c < sentence.length(); c++) {
        map.getAndIncrement(sentence.charAt(c));
    }
    return map.asMap();
}

4. Benchmarking the Approaches

JMH is a tool at our disposal to benchmark the different approaches mentioned above. To get started, we include the relevant dependencies:

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.37</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.37</version>
</dependency>

The latest versions of the JMH Core and JMH Annotation Processor can be found in Maven Central.

We wrap each approach within a Benchmark annotated method along and pass some additional parameters:

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1, warmups = 1)
public void benchContainsKeyMap() {
    IncrementMapValueWays im = new IncrementMapValueWays();
    im.charFrequencyUsingContainsKey(getString());
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1, warmups = 1)
public void benchMarkComputeMethod() {
    IncrementMapValueWays im = new IncrementMapValueWays();
    im.charFrequencyUsingCompute(getString());
}
// and so on

After running the benchmark tests, we see that the compute() and merge() methods scored better than the other approaches:

Benchmark                                      Mode  Cnt       Score       Error  Units
BenchmarkMapMethodsJMH.benchContainsKeyMap     avgt    5   50697.511 ± 25054.056  ns/op
BenchmarkMapMethodsJMH.benchMarkComputeMethod  avgt    5   45124.359 ±   377.541  ns/op
BenchmarkMapMethodsJMH.benchMarkGuavaMap       avgt    5  121372.968 ±   853.782  ns/op
BenchmarkMapMethodsJMH.benchMarkMergeMethod    avgt    5   46185.990 ±  5446.775  ns/op

We should also note that these results vary slightly and might be unnoticeable when the overall spread of the keys is not as big. As we are considering only English characters and some special characters in our example, the range of keys is capped at a few hundred. Performance would be a big concern for other scenarios where the number of keys might be large.

5. Multithreading Considerations

The approaches we discussed above do not have any multi-threading considerations. If multiple threads were reading the input String and updating a shared Map, we would most definitely be susceptible to concurrent modifications in the increment operation. This would lead to inconsistent counts in our final result.

One of the ways we can solve this is by using a ConcurrentHashMap, a thread-safe alternative to our regular HashMap. A ConcurrentHashMap provides support for concurrent operations and does not need external synchronization.

Map<Character, Integer> charMap = new ConcurrentHashMap<>();

In our solution, we should also consider that the character count increment operation that we are doing should be atomic. To ensure this, we should use atomic methods, such as compute() and merge(). 

Let’s write a test case to validate our assertion. We’ll create two threads with a shared instance of a concurrent hashmap. Each thread takes a portion of the String input and performs the same operation:

@Test
public void givenString_whenUsingConcurrentMapCompute_thenReturnFreqMap() throws InterruptedException {
    Map<Character, Integer> charMap = new ConcurrentHashMap<>();
    Thread thread1 = new Thread(() -> {
        IncrementMapValueWays ic = new IncrementMapValueWays();
        ic.charFrequencyWithConcurrentMap("the quick brown", charMap);
    });

    Thread thread2 = new Thread(() -> {
        IncrementMapValueWays ic = new IncrementMapValueWays();
        ic.charFrequencyWithConcurrentMap(" fox jumps over the lazy dog", charMap);
    });

    thread1.start();
    thread2.start();
    thread1.join();
    thread2.join();

    Map<Character, Integer> expectedMap = getExpectedMap();
    Assert.assertEquals(expectedMap, charMap);
}

6. Conclusion

In this article, we looked at different ways to increment the value of a Map entry. We benchmarked the execution speeds of the approaches and looked at how we could write a thread-safe solution as well.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)
1 Comment
Oldest
Newest
Inline Feedbacks
View all comments