Java >> Java opplæring >  >> Java

Hvordan tillate nullverdier med Collectors.toMap() i Java

Det er en kjent feil som null oppføringsverdier fungerer ikke bra med Collectors.toMap() i Java.

Anta at vi ønsker å konvertere List<Thing> list til en Map<String, String> newMap . La oss også si at hver oppføring inneholder en key og value felt (begge String ).

Denne bruken av Collectors.toMap() ville føre til en NullPointerException hvis getValue() returnerer noen gang null .

newMap = list
          .stream()
          .collect(
            Collectors.toMap(
              Thing::getKey,
              Thing::getValue
            )
          );

Hvordan kan vi omgå denne feilen og tillate null verdier i vår Map oppføringer?

1. Bruker tilpasset Collector (tillat dupliserte nøkler)

I stedet for Collectors.toMap() , kan vi bruke kartets put() funksjon for å legge til nøkkelverdioppføringer.

newMap = list
          .stream()
          .collect(
            HashMap::new, 
            (map, elem) -> map.put(
              elem.getKey(), 
              elem.getValue()
            ), 
            HashMap::putAll
          );

Denne metoden bruker collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner) .

I motsetning til Collectors.toMap() , i tilfelle av dupliserte nøkler, vil denne metoden erstatte verdiene, mens Collectors.toMap() vil kaste en IllegalStateException .

Metoden ovenfor er strømimplementering av følgende:

Map<String, String> newMap = new HashMap<>();
list.forEach((elem) -> map.put(elem.getKey(), elem.getValue()));

2. Bruker tilpasset Collector (avvis dupliserte nøkler)

Hvis vi ikke vil godta dupliserte nøkler, som med implementeringen av Collectors.toMap() , kan vi lage en tilpasset samler toMapOfNullables() .

Denne funksjonen godtar null nøkler, null verdier, og kaster IllegalStateException med dupliserte nøkler, selv når den originale nøkkelen tilordnes en null verdi (tilordninger med null verdier er forskjellige fra de uten tilordning)

public static <T, K, U> Collector<T, ?, Map<K, U>> toMapWithNullables(
  Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper
) {
  return Collectors.collectingAndThen(
    Collectors.toList(),
    list -> {
      Map<K, U> map = new LinkedHashMap<>();
      list.forEach(item -> {
        K key = keyMapper.apply(item);
        U value = valueMapper.apply(item);
        if (map.containsKey(key)) {
          throw new IllegalStateException(
            String.format(
              "Duplicate key %s (attempted merging values %s and %s)",
              key,
              map.get(key),
              value
            )
          );
        }
        map.put(key, value);
      });
      return map;
    }
  );
}

Vi kan bruke denne funksjonen akkurat som vi gjør en vanlig Collector .

newMap = list
          .stream()
          .collect(
            toMapOfNullables(
              Thing::getKey,
              Thing::getValue
            )
          );

Denne metoden bruker collect(Collector<? super T,A,R> collector) .


Java Tag