Collectors.toMapのJava 8 NullPointerException


331

Java 8 は、値の1つが「null」の場合にCollectors.toMapaをスローしNullPointerExceptionます。私はこの振る舞いを理解していません、マップは問題なく値としてnullポインターを含むことができます。値をnullにできない正当な理由はありますCollectors.toMapか?

また、これを修正するためのJava 8の優れた方法はありますか、それとも、従来のforループに戻す必要がありますか?

私の問題の例:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


class Answer {
    private int id;

    private Boolean answer;

    Answer() {
    }

    Answer(int id, Boolean answer) {
        this.id = id;
        this.answer = answer;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boolean getAnswer() {
        return answer;
    }

    public void setAnswer(Boolean answer) {
        this.answer = answer;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Answer> answerList = new ArrayList<>();

        answerList.add(new Answer(1, true));
        answerList.add(new Answer(2, true));
        answerList.add(new Answer(3, null));

        Map<Integer, Boolean> answerMap =
        answerList
                .stream()
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
    }
}

スタックトレース:

Exception in thread "main" java.lang.NullPointerException
    at java.util.HashMap.merge(HashMap.java:1216)
    at java.util.stream.Collectors.lambda$toMap$168(Collectors.java:1320)
    at java.util.stream.Collectors$$Lambda$5/1528902577.accept(Unknown Source)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1359)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at Main.main(Main.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

この問題はまだJava 11に存在します。


5
nullTreeMapのように、常に少し問題がありました。たぶん試してみるのにいい瞬間Optional<Boolean>でしょうか?それ以外の場合は分割してフィルターを使用します。
Joop Eggen 14

5
@JoopEggen nullはキーの問題になる可能性がありますが、この場合は値です。
ゴンタード2014

すべてのマップでに問題があるわけではありません。たとえばnullHashMap1つのnullキーと任意の数のnull値を持つことができるので、デフォルトの代わりにを使用してカスタムCollectorを作成してみてくださいHashMap
kajacx 14

2
@kajacxしかし、デフォルトの実装はHashMap-スタックトレースの最初の行に示されているとおりです。問題は、値をMap保持できないことではなくnullMap#merge関数の2番目の引数をnullにできないことです。
czerny 2015年

個人的には、与えられた状況では、非ストリームソリューション、または入力が並列の場合はforEach()を使用します。以下の短いストリームベースのソリューションは、ひどいパフォーマンスをもたらす可能性があります。
OndraŽižka18年

回答:


302

OpenJDKのこの既知のバグを回避するには、次のようにします。

Map<Integer, Boolean> collect = list.stream()
        .collect(HashMap::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap::putAll);

それほどきれいではありませんが、動作します。結果:

1: true
2: true
3: null

このチュートリアルが最も役に立ちました。)


3
@Jaggerはい、サプライヤの定義(最初の引数)は、パラメータを渡さずに結果を返す関数である() -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)ため、大文字と小文字を区別しないStringkeyed を作成することがラムダになりますTreeMap
Brett Ryan

2
これが正解です。代わりに、JDKがデフォルトのオーバーロードされていないバージョンに対して何をすべきかを考えてください。おそらくマージはより高速ですが、私はテストしていません。
Brett Ryan

1
コンパイルするには、型パラメーターを指定する必要がありましたMap<Integer, Boolean> collect = list.stream().collect(HashMap<Integer, Boolean>::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap<Integer, Boolean>::putAll);。私が持っていたのは:incompatible types: cannot infer type-variable(s) R (argument mismatch; invalid method reference no suitable method found for putAll(java.util.Map<java.lang.Integer,java.lang.Boolean>,java.util.Map<java.lang.Integer,java.lang.Boolean>) method java.util.Map.putAll(java.util.Map) is not applicable (actual and formal argument lists differ in length)
AnthonyO。

2
これは、大きな入力ではかなり遅くなる可能性があります。を作成してHashMapから、putAll()すべてのエントリを呼び出します。個人的には、与えられた状況では、非ストリームソリューションを使用するforEach()か、または入力が並列である場合。
OndraŽižka18年

3
このソリューションの動作は、元のtoMap実装とは異なることに注意してください。元の実装は重複キーを検出し、IllegalStatExceptionをスローしますが、このソリューションは黙って最新のキーを受け入れます。Emmanuel Touzeryのソリューション(stackoverflow.com/a/32648397/471214)は、元の動作に近いものです。
mmdemirbas

174

の静的メソッドでは不可能ですCollectors。のjavadoc は、以下に基づいてtoMap説明しtoMapていMap.mergeます。

@param mergeFunctionに提供される、同じキーに関連付けられた値間の衝突を解決するために使用されるマージ関数 Map#merge(Object, Object, BiFunction)}

そしてMap.merge言うのjavadoc :

@throws NullPointerException-指定されたキーがnullであり、このマップがnullキーをサポートしていない場合またはまたはremappingFunction nullの場合

forEachリストのメソッドを使用すると、forループを回避できます。

Map<Integer,  Boolean> answerMap = new HashMap<>();
answerList.forEach((answer) -> answerMap.put(answer.getId(), answer.getAnswer()));

しかし、これは古い方法ほど単純ではありません。

Map<Integer, Boolean> answerMap = new HashMap<>();
for (Answer answer : answerList) {
    answerMap.put(answer.getId(), answer.getAnswer());
}

3
その場合は、昔ながらのfor-eachを使用します。これをtoMergeのバグと見なす必要がありますか?このマージ関数の使用は実際には実装の詳細であるか、またはtoMapがnull値を処理することを許可しないための適切な理由ですか?
Jasper

6
マージのjavadocで指定されていますが、toMapのドキュメントには記載されていません
Jasper

119
マップ内のnull値が標準APIにそのような影響を与えるとは考えていませんでした。むしろ欠陥として考えたいと思います。
アスカルカリコフ2015

16
実際、APIドキュメントはの使用について何も述べていませんMap.merge。このIMHOは、見過ごされてきた完全に許容可能なユースケースを制限する実装上の欠陥です。オーバーロードされたメソッドtoMapDO状態の使用Map.mergeではなく、OPが使用している1。
Brett Ryan

11
@Jasperバグレポートさえありますbugs.openjdk.java.net/browse/JDK-8148463
pixel

23

私が書いCollectorた、あなたが持っているときに、デフォルトのJava 1とは異なり、クラッシュしないnull値を:

public static <T, K, U>
        Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
                Function<? super T, ? extends U> valueMapper) {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                Map<K, U> result = new HashMap<>();
                for (T item : list) {
                    K key = keyMapper.apply(item);
                    if (result.putIfAbsent(key, valueMapper.apply(item)) != null) {
                        throw new IllegalStateException(String.format("Duplicate key %s", key));
                    }
                }
                return result;
            });
}

Collectors.toMap()呼び出しをこの関数の呼び出しに置き換えるだけで、問題が修正されます。


1
ただし、null値を許可して使用することputIfAbsentは、うまく機能しません。null… にマップするときに重複キーを検出しません
Holger

10

うん、私からの遅い答えですが、誰かが他のCollectorロジックをコーディングしたい場合に備えて、内部で何が起こっているのかを理解することは役立つと思います。

私は、よりネイティブで直接的なアプローチをコーディングすることで問題を解決しようとしました。私はそれが可能な限り直接的だと思います:

public class LambdaUtilities {

  /**
   * In contrast to {@link Collectors#toMap(Function, Function)} the result map
   * may have null values.
   */
  public static <T, K, U, M extends Map<K, U>> Collector<T, M, M> toMapWithNullValues(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
    return toMapWithNullValues(keyMapper, valueMapper, HashMap::new);
  }

  /**
   * In contrast to {@link Collectors#toMap(Function, Function, BinaryOperator, Supplier)}
   * the result map may have null values.
   */
  public static <T, K, U, M extends Map<K, U>> Collector<T, M, M> toMapWithNullValues(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Supplier<Map<K, U>> supplier) {
    return new Collector<T, M, M>() {

      @Override
      public Supplier<M> supplier() {
        return () -> {
          @SuppressWarnings("unchecked")
          M map = (M) supplier.get();
          return map;
        };
      }

      @Override
      public BiConsumer<M, T> accumulator() {
        return (map, element) -> {
          K key = keyMapper.apply(element);
          if (map.containsKey(key)) {
            throw new IllegalStateException("Duplicate key " + key);
          }
          map.put(key, valueMapper.apply(element));
        };
      }

      @Override
      public BinaryOperator<M> combiner() {
        return (left, right) -> {
          int total = left.size() + right.size();
          left.putAll(right);
          if (left.size() < total) {
            throw new IllegalStateException("Duplicate key(s)");
          }
          return left;
        };
      }

      @Override
      public Function<M, M> finisher() {
        return Function.identity();
      }

      @Override
      public Set<Collector.Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
      }

    };
  }

}

そして、JUnitとassertjを使用したテスト:

  @Test
  public void testToMapWithNullValues() throws Exception {
    Map<Integer, Integer> result = Stream.of(1, 2, 3)
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null));

    assertThat(result)
        .isExactlyInstanceOf(HashMap.class)
        .hasSize(3)
        .containsEntry(1, 1)
        .containsEntry(2, null)
        .containsEntry(3, 3);
  }

  @Test
  public void testToMapWithNullValuesWithSupplier() throws Exception {
    Map<Integer, Integer> result = Stream.of(1, 2, 3)
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null, LinkedHashMap::new));

    assertThat(result)
        .isExactlyInstanceOf(LinkedHashMap.class)
        .hasSize(3)
        .containsEntry(1, 1)
        .containsEntry(2, null)
        .containsEntry(3, 3);
  }

  @Test
  public void testToMapWithNullValuesDuplicate() throws Exception {
    assertThatThrownBy(() -> Stream.of(1, 2, 3, 1)
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null)))
            .isExactlyInstanceOf(IllegalStateException.class)
            .hasMessage("Duplicate key 1");
  }

  @Test
  public void testToMapWithNullValuesParallel() throws Exception {
    Map<Integer, Integer> result = Stream.of(1, 2, 3)
        .parallel() // this causes .combiner() to be called
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null));

    assertThat(result)
        .isExactlyInstanceOf(HashMap.class)
        .hasSize(3)
        .containsEntry(1, 1)
        .containsEntry(2, null)
        .containsEntry(3, 3);
  }

  @Test
  public void testToMapWithNullValuesParallelWithDuplicates() throws Exception {
    assertThatThrownBy(() -> Stream.of(1, 2, 3, 1, 2, 3)
        .parallel() // this causes .combiner() to be called
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null)))
            .isExactlyInstanceOf(IllegalStateException.class)
            .hasCauseExactlyInstanceOf(IllegalStateException.class)
            .hasStackTraceContaining("Duplicate key");
  }

そして、それをどのように使用しますか?まあ、toMap()テストが示すように代わりにそれを使用してください。これにより、呼び出しコードが可能な限りきれいに見えます。

編集:
以下のホルガーのアイデアを実装し、テストメソッドを追加しました


1
コンバイナは重複キーをチェックしません。すべてのキーのチェックを避けたい場合は、次のようなものを使用できます(map1, map2) -> { int total = map1.size() + map2.size(); map1.putAll(map2); if(map1.size() < total.size()) throw new IllegalStateException("Duplicate key(s)"); return map1; }
Holger

@ホルガーうん、そうだね。特にaccumulator()実際にそれをチェックするので。多分私はいくつかの並列ストリームを一度行うべきです:)
sjngm

7

@EmmanuelTouzeryによって提案されたものよりもやや単純なコレクターを以下に示します。必要に応じて使用してください:

public static <T, K, U> Collector<T, ?, Map<K, U>> toMapNullFriendly(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends U> valueMapper) {
    @SuppressWarnings("unchecked")
    U none = (U) new Object();
    return Collectors.collectingAndThen(
            Collectors.<T, K, U> toMap(keyMapper,
                    valueMapper.andThen(v -> v == null ? none : v)), map -> {
                map.replaceAll((k, v) -> v == none ? null : v);
                return map;
            });
}

nullカスタムオブジェクトに置き換えてnone、フィニッシャーで逆の操作を行うだけです。


5

値が文字列の場合、これは機能する可能性があります。 map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> Optional.ofNullable(e.getValue()).orElse("")))


4
これは、データの変更に問題がない場合にのみ機能します。下流のメソッドは、空の文字列ではなくnull値を期待する場合があります。
Sam Buchmiller

3

による Stacktrace

Exception in thread "main" java.lang.NullPointerException
at java.util.HashMap.merge(HashMap.java:1216)
at java.util.stream.Collectors.lambda$toMap$148(Collectors.java:1320)
at java.util.stream.Collectors$$Lambda$5/391359742.accept(Unknown Source)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1359)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.guice.Main.main(Main.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

と呼ばれるとき map.merge

        BiConsumer<M, T> accumulator
            = (map, element) -> map.merge(keyMapper.apply(element),
                                          valueMapper.apply(element), mergeFunction);

null最初にチェックを行います

if (value == null)
    throw new NullPointerException();

私はJava 8をあまり頻繁に使用しないので、それを修正するためのより良い方法があるかどうかはわかりませんが、修正は少し難しいです。

あなたがすることができます:

filterを使用してすべてのNULL値をフィルタリングします。JavaScriptコードで、サーバーがこのIDに対する応答を送信していないかどうかを確認すると、サーバーが応答しなかったことを意味します。

このようなもの:

Map<Integer, Boolean> answerMap =
        answerList
                .stream()
                .filter((a) -> a.getAnswer() != null)
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

または、要素のストリーム要素を変更するために使用されるpeekを使用します。プレビューを使用すると、答えをマップに適したものに変更できますが、ロジックを少し編集する必要があります。

現在のデザインを維持したい場合は避けた方がいい Collectors.toMap


3

Emmanuel Touzeryの実装を少し変更しました。

このバージョン。

  • nullキーを許可
  • null値を許可
  • 重複するキーを検出し(それらがnullであっても)、元のJDK実装と同様にIllegalStateExceptionをスローします。
  • キーがすでにnull値にマッピングされている場合も、重複キーを検出します。つまり、null値のマッピングをマッピングなしから分離します。
public static <T, K, U> Collector<T, ?, Map<K, U>> toMapOfNullables(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);
                if (map.containsKey(key)) {
                    throw new IllegalStateException(String.format("Duplicate key %s", key));
                }
                map.put(key, valueMapper.apply(item));
            });
            return map;
        }
    );
}

単体テスト:

@Test
public void toMapOfNullables_WhenHasNullKey() {
    assertEquals(singletonMap(null, "value"),
        Stream.of("ignored").collect(Utils.toMapOfNullables(i -> null, i -> "value"))
    );
}

@Test
public void toMapOfNullables_WhenHasNullValue() {
    assertEquals(singletonMap("key", null),
        Stream.of("ignored").collect(Utils.toMapOfNullables(i -> "key", i -> null))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateNullKeys() {
    assertThrows(new IllegalStateException("Duplicate key null"),
        () -> Stream.of(1, 2, 3).collect(Utils.toMapOfNullables(i -> null, i -> i))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateKeys_NoneHasNullValue() {
    assertThrows(new IllegalStateException("Duplicate key duplicated-key"),
        () -> Stream.of(1, 2, 3).collect(Utils.toMapOfNullables(i -> "duplicated-key", i -> i))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateKeys_OneHasNullValue() {
    assertThrows(new IllegalStateException("Duplicate key duplicated-key"),
        () -> Stream.of(1, null, 3).collect(Utils.toMapOfNullables(i -> "duplicated-key", i -> i))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateKeys_AllHasNullValue() {
    assertThrows(new IllegalStateException("Duplicate key duplicated-key"),
        () -> Stream.of(null, null, null).collect(Utils.toMapOfNullables(i -> "duplicated-key", i -> i))
    );
}

1

古い質問をもう一度開いて申し訳ありませんが、最近編集されたため、「問題」がまだJava 11に残っていると言っています。これを指摘したいと思いました。

answerList
        .stream()
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

マップはnullを値として許可しないため、nullポインタ例外が発生します。これは、キーのマップを調べてkも存在しない場合、戻り値はすでに存在するためnull(javadocを参照)、理にかなっています。したがってk、値を入力できた場合null、マップは奇妙に動作しているように見えます。

誰かがコメントで言ったように、フィルタリングを使用してこれを解決するのは非常に簡単です:

answerList
        .stream()
        .filter(a -> a.getAnswer() != null)
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

この方法では、null値はマップに挿入されnullず、マップで回答のないIDを検索すると、「値」として取得されます。

これが誰にとっても理にかなっているといいのですが。


1
マップがnull値を許可しない場合は理にかなっていますが、許可されています。answerMap.put(4, null);問題なく行えます。提案されたソリューションでは、値がnullとして挿入される場合と同じように、anserMap.get()が存在しない場合でも同じ結果が得られます。ただし、マップのすべてのエントリを反復処理する場合、明らかに違いがあります。
Jasper

1
public static <T, K, V> Collector<T, HashMap<K, V>, HashMap<K, V>> toHashMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper
)
{
    return Collector.of(
            HashMap::new,
            (map, t) -> map.put(keyMapper.apply(t), valueMapper.apply(t)),
            (map1, map2) -> {
                map1.putAll(map2);
                return map1;
            }
    );
}

public static <T, K> Collector<T, HashMap<K, T>, HashMap<K, T>> toHashMap(
        Function<? super T, ? extends K> keyMapper
)
{
    return toHashMap(keyMapper, Function.identity());
}

1
これはコンパイルされるため、賛成票を投じます。Map :: putAllには戻り値がないため、受け入れられた回答はコンパイルされません。
Taugenichts

0

小さな調整ですべての質問IDを保持する

Map<Integer, Boolean> answerMap = 
  answerList.stream()
            .collect(Collectors.toMap(Answer::getId, a -> 
                       Boolean.TRUE.equals(a.getAnswer())));

これが最良の答えだと思います。これは最も簡潔な答えであり、NPEの問題を修正します。
LConrad、2018

-3

NullPointerExceptionは、最も頻繁に発生する例外です(少なくとも私の場合)。これを回避するために、私は防御的になり、一連のnullチェックを追加し、肥大化した醜いコードになってしまいます。Java 8では、null参照を処理するためのOptionalが導入されているため、null許容値と非null値を定義できます。

そうは言っても、私はすべてのnull可能な参照をOptionalコンテナーにラップします。また、下位互換性も壊すべきではありません。これがコードです。

class Answer {
    private int id;
    private Optional<Boolean> answer;

    Answer() {
    }

    Answer(int id, Boolean answer) {
        this.id = id;
        this.answer = Optional.ofNullable(answer);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets the answer which can be a null value. Use {@link #getAnswerAsOptional()} instead.
     *
     * @return the answer which can be a null value
     */
    public Boolean getAnswer() {
        // What should be the default value? If we return null the callers will be at higher risk of having NPE
        return answer.orElse(null);
    }

    /**
     * Gets the optional answer.
     *
     * @return the answer which is contained in {@code Optional}.
     */
    public Optional<Boolean> getAnswerAsOptional() {
        return answer;
    }

    /**
     * Gets the answer or the supplied default value.
     *
     * @return the answer or the supplied default value.
     */
    public boolean getAnswerOrDefault(boolean defaultValue) {
        return answer.orElse(defaultValue);
    }

    public void setAnswer(Boolean answer) {
        this.answer = Optional.ofNullable(answer);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Answer> answerList = new ArrayList<>();

        answerList.add(new Answer(1, true));
        answerList.add(new Answer(2, true));
        answerList.add(new Answer(3, null));

        // map with optional answers (i.e. with null)
        Map<Integer, Optional<Boolean>> answerMapWithOptionals = answerList.stream()
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswerAsOptional));

        // map in which null values are removed
        Map<Integer, Boolean> answerMapWithoutNulls = answerList.stream()
                .filter(a -> a.getAnswerAsOptional().isPresent())
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

        // map in which null values are treated as false by default
        Map<Integer, Boolean> answerMapWithDefaults = answerList.stream()
                .collect(Collectors.toMap(a -> a.getId(), a -> a.getAnswerOrDefault(false)));

        System.out.println("With Optional: " + answerMapWithOptionals);
        System.out.println("Without Nulls: " + answerMapWithoutNulls);
        System.out.println("Wit Defaults: " + answerMapWithDefaults);
    }
}

1
役に立たない答え、なぜあなたはこれを修正するためにnullを取り除くべきですか?これはCollectors.toMap()null値ではない問題です
Enerccio

@Enerccio友達を落ち着かせる!! null値に依存することはお勧めできません。Optionalを使用した場合、最初はNPEに遭遇しなかったでしょう。オプションの使用法を読んでください。
TriCore

1
なんで?Null値は問題ありません。問題となっているのはドキュメント化されていないライブラリです。オプションは良いですが、どこでもありません。
Enerccio
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.