아래와 같은 toMap 코드를 사용하면 NPE 에러가 발핸다.


    final Map<String, Object> valueMap = pingFailedHosts

        .stream()

        .collect(Collectors.toMap(c->c, null));

        


toMap 메소드는 KeyMapper와 ValueMapper를 전달하는 형태인데. 

public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}


toMap 원형 함수를 보면, mergeFunction , mapSupplier가 추가된다. 

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> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}


https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html



toMap 메소드의 매개변수를 보면, mergeFunction이 Map.merge()와 연관되어 있음을 알 수 있다. 

Parameters:
keyMapper - a mapping function to produce keys
valueMapper - a mapping function to produce values
mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction)
mapSupplier - a function which returns a new, empty Map into which the results will be inserted







코드를 따라가 들어가면


private static <K, V, M extends Map<K,V>>
BinaryOperator<M> mapMerger(BinaryOperator<V> mergeFunction) {
return (m1, m2) -> {
for (Map.Entry<K,V> e : m2.entrySet())
m1.merge(e.getKey(), e.getValue(), mergeFunction);
return m1;
};
}


m1.merge() 메소드가 바로 Map.merge() 메소드이다.


여기 코드 보면, value 값이 not null이어야 한다. 


default V merge(K key, V value,
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);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}


코드의 주석이나, 오라클 javadoc 문서를 보면 역시 NPE를 발행하는 코드임을 확인할 수 있다.


NullPointerException - if the specified key is null and this map does not support null keys or the value or remappingFunction is null





따라서 key나 value의 값이 null일 확률이 높다면, toMap()를 사용하지 말아야 한다.


아래와 같이 naive하게 개발하는 것이 좋다.


    final Map<String, String> valueMap2 = Arrays.asList("cacti.google.com", "sun.google.com")

        .stream()

        .collect(HashMap::new, (m,v)->m.put(v, null), HashMap::putAll);




Posted by '김용환'
,