Backend Development 6 min read

How to Use Java Stream toMap with Duplicate Keys and Null Values

This article demonstrates how to convert a list of Java objects into a Map using Stream's toMap collector, addresses duplicate key exceptions by providing a merge function, handles null values with Optional, and compares stream-based solutions with traditional for-loop approaches.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
How to Use Java Stream toMap with Duplicate Keys and Null Values

Java 8 introduced the Stream API, which has become essential for modern Java development. One common task is converting a collection of objects into a Map using Collectors.toMap .

First, define a simple user entity:

@Data
@AllArgsConstructor
public class User {
    private int id;
    private String name;
}

Attempting to collect a list of User objects into a map with the default collector can raise an IllegalStateException when duplicate keys are present:

public class UserTest {
    @Test
    public void demo() {
        List
userList = new ArrayList<>();
        // 模拟数据
        userList.add(new User(1, "Alex"));
        userList.add(new User(1, "Beth"));
        Map
map = userList.stream()
                .collect(Collectors.toMap(User::getId, User::getName));
        System.out.println(map);
    }
}

Because two users share the same id , the collector throws an exception. Providing a merge function resolves the duplicate‑key problem, but a NullPointerException can still occur if a name is null :

public class UserTest {
    @Test
    public void demo() {
        List
userList = new ArrayList<>();
        // 模拟数据
        userList.add(new User(1, "Alex"));
        userList.add(new User(2, null));
        Map
map = userList.stream()
                .collect(Collectors.toMap(User::getId, User::getName, (oldData, newData) -> newData));
        System.out.println(map);
    }
}

To safely handle null values, wrap the value extraction with Optional and provide a default, while still merging duplicates:

public class UserTest {
    @Test
    public void demo() {
        List
userList = new ArrayList<>();
        // 模拟数据
        userList.add(new User(1, "Alex"));
        userList.add(new User(1, "Beth"));
        userList.add(new User(2, null));
        Map
map = userList.stream()
                .collect(Collectors.toMap(
                    User::getId,
                    it -> Optional.ofNullable(it.getName()).orElse(""),
                    (oldData, newData) -> newData
                ));
        System.out.println(map);
    }
}

For comparison, the same result can be achieved with a classic for loop:

public class UserTest {
    @Test
    public void demo() {
        List
userList = new ArrayList<>();
        // 模拟数据
        userList.add(new User(1, "Alex"));
        userList.add(new User(1, "Beth"));
        userList.add(new User(2, null));
        Map
map = new HashMap<>();
        userList.forEach(it -> {
            map.put(it.getId(), it.getName());
        });
        System.out.println(map);
    }
}

These examples illustrate how to handle duplicate keys and null values when using Collectors.toMap , and show that Stream‑based solutions combined with Optional provide concise, expressive alternatives to manual loops.

JavaStreamOptionalnullpointerexceptionDuplicateKeytoMap
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

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.