~/tech-blog/posts/java-stream-duplicate-key — zsh
지금 --명이 보고 있습니다오늘 --누적 --

Collectors.toMap에서 만난 Duplicate Key 예외

Stream의 Collectors.toMap()으로 컬렉션을 Map으로 변환하면서 발생한 Duplicate Key 예외에 대해 정리하도록 하겠습니다.

발생 원인

Java 8에서 제공하는 Stream API의 Collectors 클래스의 toMap() 메서드는 스트림의 요소를 모아 Map을 반환합니다.

다만 이 메서드는 스트림에서 처리하는 요소 중에 키가 중복되는 경우 Duplicate Key Error(IllegalStateException)를 발생시킵니다.

Java
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
map.put("D", 4);
map.put("D", 5);

// Duplicate key error: java.lang.IllegalStateException: Duplicate key 4
//Map<String, Integer> result = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

위 예제는 키 D에 값 4와 5를 넣어 중복 키를 표현한 코드입니다. 다만 HashMap은 같은 키로 put을 하면 이전 값을 새 값으로 덮어쓰기 때문에, 실제로 map에는 D=5 하나만 남고 entrySet()의 스트림에서는 키가 중복되지 않습니다. 중복 키를 직접 확인하려면 아래처럼 같은 키를 가진 요소가 스트림에 두 번 들어오도록 만들어야 합니다.

Java
// Java 9 이상 (Map.entry)
Map<String, Integer> result = Stream.of(
                Map.entry("A", 1), Map.entry("B", 2), Map.entry("C", 3),
                Map.entry("D", 4), Map.entry("D", 5))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

// java.lang.IllegalStateException: Duplicate key D (attempted merging values 4 and 5)

예외 메시지는 Java 버전에 따라 다릅니다. Java 8에서는 원래 예제의 주석처럼 Duplicate key 4로 키 대신 먼저 들어간 값이 출력되고, Java 9부터는 키와 병합하려던 두 값이 함께 출력됩니다.

toMap이 요소를 하나씩 Map에 넣다가 같은 키를 만났을 때 어떻게 되는지 그리면 아래와 같습니다.

Merge Function이 없을 때와 있을 때 중복 키 D의 처리 흐름눌러서 원본 크기로 보기

Merge Function이 없으면 두 번째 D에서 예외가 발생하고, Merge Function이 있으면 두 값을 병합한 결과가 저장되는 것을 확인할 수 있습니다.

해결 방법

이러한 오류가 발생하는 경우에는 toMap() 메서드의 세 번째 매개변수로 Merge Function을 전달하여 해결합니다. 첫 번째와 두 번째 매개변수는 키와 값을 만드는 함수입니다.

Merge Function은 중복 키가 발생할 때 값을 병합하는 방법을 정의합니다.

아래는 toMap() 메서드에서 Merge Function을 사용하여 중복 키 오류를 해결하는 방법입니다.

Java
Map<String, Integer> result = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1 + v2));

System.out.println(result); // {A=1, B=2, C=3, D=9}

(v1, v2) -> v1 + v2는 먼저 들어간 값(v1)과 새로 들어온 값(v2)을 더하는 함수입니다. 키 D에 4와 5가 차례로 들어오면 9가 저장됩니다. 다만 위 코드처럼 HashMap의 entrySet()을 스트림으로 만들면 앞에서 설명한 것처럼 D=5만 남아 있으므로, 실제 출력은 {A=1, B=2, C=3, D=5}입니다. D=9는 앞의 Stream.of(...) 예제처럼 중복 키가 스트림에 들어올 때의 결과입니다.

마무리

오늘은 컬렉션 중에 toMap을 할 때 자주 발생할 수 있는 Duplicate Key Error에 대하여 정리하였습니다. 컬렉션이 맵으로 변경되기 쉬운 만큼 조건을 잘 걸어줘야 하며, 추가로 null도 filter로 걸러주는 게 좀 더 안정적인 방법이지 않을까 싶습니다.

댓글

GitHub 계정으로 로그인하시면 댓글과 질문을 남기실 수 있습니다. 남겨 주신 글은 이 저장소의 Discussions에 쌓입니다.

● main 231 posts UTF-8 LF © 2026 코징 RSS · GitHub