본문 바로가기
Develop/Java

[Java] Duplicate key XXX 에러

by 연로그 2022. 5. 18.
반응형

java.lang.IllegalStateException: Duplicate key 'key로 저장하려는 값'
(attempted merging values ~~)


❓ 에러 원인

  • Collectors.toMap() 호출 시 중복되는 key 값이 존재

 

💡 해결 방법

해결 방법은 크게 2가지가 있다.

 

  1. key가 중복되지 않는 데이터 사용
  2. toMap(Function, Function) 대신 toMap(Function, Function, BinaryOperatior) 사용

 

👀 더 자세히 살펴보기

위와 같은 에러가 발생하는 예제 코드를 만들어봤다.

name을 key로, User 객체 자체를 value로 넣으려고 한다.

헌데 name이 동일한 데이터가 2개 존재한다.

 

key만 동일한 데이터가 존재하는 경우

@Test
void mapTest() {
    List<User> users = List.of(new User("yeonlog", 25), new User("yeonlog", 26));
    Map<String, User> userMap = users.stream()
            .collect(Collectors.toMap(User::getName, user -> user));
}

 

toMap의 메서드 설명을 가져와봤다.

Returns a Collector that accumulates elements into a Map whose keys and values are the result of applying the provided mapping functions to the input elements. If the mapped keys contain duplicates (according to Object.equals(Object)), an IllegalStateException is thrown when the collection operation is performed. If the mapped keys might have duplicates, use toMap(Function, Function, BinaryOperator) instead.

 

마지막 문장을 보면 key가 중복될 수도 있다면 toMap(Function, Function, BinaryOperator)를 사용하라고 한다.

BinaryOperator 인자를 보내면 중복되는 데이터가 존재하는 경우 해당 인자를 이용한다.

예를 들어 Map에 먼저 put했던 데이터를 저장하고 싶으면 아래와 같이 표현할 수 있다.

Map<String, User> userMap = users.stream()
        .collect(Collectors.toMap(User::getName, user -> user, (user1, user2) -> user1));


반대로 나중에 호출되는 데이터를 Map에 저장하고 싶으면 아래와 같이 표현할 수 있다.

Map<String, User> userMap = users.stream()
        .collect(Collectors.toMap(User::getName, user -> user, (user1, user2) -> user2));

 

 

동일한 데이터가 존재하는 경우

아래와 같이 완전히 일치하는 데이터가 여러개 존재할 수 있다.

이 경우에는 해결하기 더 쉽다.

List<User> users = List.of(new User("yeonlog", 25), new User("yeonlog", 25));

 

stream의 distinct()를 이용해서 중복을 제거한 뒤, Collectors.toMap을 이용하면 된다.

단, Object.equals()를 활용해 비교하므로 equals와 hashCode 재정의가 필수적이다.

Map<String, User> userMap = users.stream()
    .distinct()
    .collect(Collectors.toMap(User::getName, user -> user));

 

반응형