Home > Software > Handling Duplicate Keys When Producing Maps Using Java Stream

Handling Duplicate Keys When Producing Maps Using Java Stream

Anastasios Antoniadis

Learn how to handle duplicate keys gracefully when producing maps using Java Stream. This guide explores strategies like choosing or combining duplicates and collecting them into custom collections, providing practical examples to ensure robust and error-free code in your Java applications.

Java

The Stream API in Java 8 and onwards has transformed how we manipulate collections, making operations more expressive and less verbose. One common task is to transform a list into a map for quick lookups. However, a challenge arises when the list contains elements that result in duplicate keys in the target map. By default, the toMap collector throws an IllegalStateException when duplicate keys are encountered. This article explores strategies to handle duplicate keys gracefully when producing maps using Java Stream, ensuring robust and error-free code.

Understanding the Problem

Consider a list of Person objects where each Person has a name and age. Our goal is to create a map where each person’s name is a key, and their age is the value.

List<Person> people = Arrays.asList(
    new Person("John", 30),
    new Person("Jane", 25),
    new Person("John", 28) // Duplicate key "John"
);

Attempting to convert this list into a map using the straightforward approach results in an exception:

Map<String, Integer> nameToAgeMap = people.stream()
    .collect(Collectors.toMap(Person::getName, Person::getAge));

The code above will throw IllegalStateException because of the duplicate key “John”.

Strategy 1: Choosing One of the Duplicates

One way to handle duplicates is to decide on a policy for which value to keep. The toMap method allows you to specify a merge function, which determines how to deal with duplicates.

Keeping the First Encountered Value

You might decide that the first occurrence is the most relevant one.

Map<String, Integer> nameToAgeMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (existingValue, newValue) -> existingValue
    ));

In this case, the age “30” for “John” will be in the map, and the duplicate “John” with age “28” will be ignored.

Keeping the Last Encountered Value

Alternatively, you might prefer the last encountered value:

Map<String, Integer> nameToAgeMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (existingValue, newValue) -> newValue
    ));

Now, the map will contain “John” with age “28”, ignoring the first occurrence.

Strategy 2: Combining Duplicate Values

Sometimes, discarding duplicates isn’t ideal, and a better approach is combining them somehow.

Summing Up Values

If the values are numeric, you might sum them up:

Map<String, Integer> nameToTotalAge = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        Integer::sum
    ));

This results in a map where “John” is associated with the age “58”, the sum of “30” and “28”.

Collecting Duplicates into a List

For non-numeric values or when you want to preserve all duplicate entries, you can collect them into a list:

Map<String, List<Integer>> nameToAges = people.stream()
    .collect(Collectors.groupingBy(
        Person::getName,
        Collectors.mapping(Person::getAge, Collectors.toList())
    ));

This approach creates a map where each key is associated with a list of ages, handling duplicates gracefully.

Strategy 3: Custom Collection Types

Java’s Collectors utility provides toMap, but sometimes you might need a more specialized collection, like a Set for unique values or even a custom type for complex merge logic.

Using a Set to Avoid Duplicates

If the goal is to ensure each key maps to a unique set of values, consider using a Set:

Map<String, Set<Integer>> nameToAgeSet = people.stream()
    .collect(Collectors.groupingBy(
        Person::getName,
        Collectors.mapping(Person::getAge, Collectors.toSet())
    ));

This produces a map where each name is associated with a set of unique ages.

Conclusion

Handling duplicate keys when producing maps with Java Stream is a common challenge that requires thoughtful consideration of how duplicates should be managed. Whether choosing one of the duplicates, combining them, or collecting them into a custom collection, Java Stream provides the flexibility to handle duplicates in a way that best fits the application’s needs. By leveraging the power of collectors and merge functions, developers can ensure their map transformations are robust and their applications are free from unexpected IllegalStateExceptions.

Anastasios Antoniadis
Follow me
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x