[ Java8 ] Stream Collectors toMap 중복키처리하기(Duplicate Key error)
Collection.toMap()
함수에서 key 가 중복되는 경우 어떻게 처리하는지에 그 소스 자체의 정책에 따라 에러가 발생한다고 합니다.public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return new CollectorImpl<>(HashMap::new,
uniqKeysMapAccumulator(keyMapper, valueMapper),
uniqKeysMapMerger(),
CH_ID);
}
Map<Long, ProductEntity> productMap = foundProducts.stream()
.collect(Collectors.toMap(ProductEntity::getId, product -> product));
왼쪽 파라미터가 keyMapper
오른쪽 파라미터가 valueMapper
각 입력 요소를 uniqKeysMapAccumulator
메서드를 통해
private static <T, K, V> BiConsumer<Map<K, V>, T> uniqKeysMapAccumulator(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
return (map, element) -> {
K k = keyMapper.apply(element);
V v = Objects.requireNonNull(valueMapper.apply(element));
V u = map.putIfAbsent(k, v);
if (u != null) throw duplicateKeyException(k, u, v);
};
}
내부적으로 Map<K,V> 타입의 맵과 T 타입의 요소를 반환한다.
(p1, p2) -> p1
를 해주는 이유가?(p1, p2) -> p1
구문은 충돌이 발생한 경우 Map<Long, ProductEntity> productMap = foundProducts.stream()
.collect(Collectors.toUnmodifiableMap(ProductEntity::getId, product -> product, (p1, p2) -> p1));
이런식으로 수정하고,
사용되는 메서드는 이런 메서드로 변경이 됩니다
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapFactory) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapFactory, accumulator, mapMerger(mergeFunction), CH_ID);
}
merge 메서드안에서
default V merge(K key, V valㅁue,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key); // 추가하는 키값
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
// 만약 이전 키에 대한 값이 존재하지 않으면 그냥 newKey, newValue 값으로 맵에 추가
// 이전 키에 대한 값이 존재한다면, remappingFunction 으로 (p1 , p2 ) -> p1 ... oldValue 에 대한 값을 반환한다.
// 즉 , key, value 에는 이전에 존재하던 값이 그대로 유지됩니다.
if (newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}