Backend Development 7 min read

Mastering Java Stream toMap: Handling Duplicate Keys and Null Values

This article explains how to convert a list of Java objects into a Map using Stream's toMap collector, addresses duplicate key exceptions, demonstrates providing a merge function, handling null values with Optional, and compares alternative approaches such as manual loops, offering complete code examples.

macrozheng
macrozheng
macrozheng
Mastering Java Stream toMap: Handling Duplicate Keys and Null Values

In JDK 8, Java introduced the Stream API, which has become essential for daily development.

When you need to collect a stream into a collection, you may use

collect(Collectors.toList())

or

collect(Collectors.toSet())

. However, converting a list of objects to a

Map

with

toMap()

can cause problems when keys are duplicated.

Consider a

User

class with

id

and

name

. If you read a list of

User

objects from a database and try to build a map where the key is

id

and the value is

name

, the following code will throw an

IllegalStateException

because the key

1

appears twice.

<code>@Data
@AllArgsConstructor
public class User {
    private int id;
    private String name;
}
</code>
<code>public class UserTest {
    @Test
    public void demo() {
        List<User> userList = new ArrayList<>();
        // mock data
        userList.add(new User(1, "Alex"));
        userList.add(new User(1, "Beth"));
        Map<Integer, String> map = userList.stream()
                .collect(Collectors.toMap(User::getId, User::getName));
        System.out.println(map);
    }
}
</code>

The duplicate key causes the exception. One simple fix is to provide a merge function as the third argument of

Collectors.toMap

, which decides how to handle conflicts.

<code>Map<Integer, String> map = userList.stream()
        .collect(Collectors.toMap(User::getId,
                User::getName,
                (oldData, newData) -> newData));
</code>

If the value may be

null

, you can combine

Optional

to supply a default value:

<code>Map<Integer, String> map = userList.stream()
        .collect(Collectors.toMap(User::getId,
                it -> Optional.ofNullable(it.getName()).orElse(""),
                (oldData, newData) -> newData));
</code>

Alternatively, you can fall back to a classic loop:

<code>Map<Integer, String> map = new HashMap<>();
userList.forEach(it -> map.put(it.getId(), it.getName()));
</code>

These approaches eliminate the duplicate‑key exception and handle null values gracefully, allowing you to choose between a functional Stream style or an imperative loop based on your preference.

BackendJavaStreamOptionalduplicate-keycollectorstoMap
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.