map()とflatMap()の違い
1. 概要
map()およびflatMap() APIは、関数型言語に由来します。 Java 8では、それらはOptional, StreamとCompletableFuture (althoughでわずかに異なる名前で見つけることができます)。
Streamsはオブジェクトのシーケンスを表しますが、オプションは存在または不在の値を表すクラスです。 他の集計操作の中には、map()メソッドとflatMap()メソッドがあります。
両方が同じ戻り値の型,を持っているという事実にもかかわらず、それらはかなり異なります。 ストリームとオプションの例をいくつか分析して、これらの違いを説明しましょう。
参考文献:
2. オプションのマップとフラットマップ
map()メソッドはOptionalでうまく機能します–関数が必要な正確な型を返す場合:
Optional s = Optional.of("test");
assertEquals(Optional.of("TEST"), s.map(String::toUpperCase));
ただし、より複雑なケースでは、Optionalも返す関数が与えられる場合があります。 このような場合、map()の実装は内部で追加のラッピングを行うため、map()を使用するとネストされた構造になります。
この状況をよりよく理解するために、別の例を見てみましょう。
assertEquals(Optional.of(Optional.of("STRING")),
Optional
.of("string")
.map(s -> Optional.of("STRING")));
ご覧のとおり、最終的にネストされた構造体Optional<Optional<String>>.になります。これは機能しますが、使用するのがかなり面倒で、追加のnullセーフ性がないため、フラットな構造体を維持することをお勧めします。
それがまさにflatMap()が私たちに役立つことです。
assertEquals(Optional.of("STRING"), Optional
.of("string")
.flatMap(s -> Optional.of("STRING")));
3. ストリームのマップとフラットマップ
どちらの方法もOptionalに対して同様に機能します。
map()メソッドは、基になるシーケンスをStreamインスタンスでラップしますが、flatMap()メソッドでは、ネストされたStream<Stream<R>>構造を回避できます。
次の例では、map()は、入力Stream:の要素にtoUpperCase()メソッドを適用した結果で構成されるStreamを生成します。
List myList = Stream.of("a", "b")
.map(String::toUpperCase)
.collect(Collectors.toList());
assertEquals(asList("A", "B"), myList);
map()は、このような単純なケースでは非常にうまく機能しますが、入力としてリストのリストなど、より複雑なものがある場合はどうでしょうか。
仕組みを見てみましょう。
List> list = Arrays.asList(
Arrays.asList("a"),
Arrays.asList("b"));
System.out.println(list);
このスニペットは、リスト[[a], [b]].のリストを出力します。次に、flatMap()を使用しましょう。
System.out.println(list
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList()));
このようなスニペットの結果は、[a, b].にフラット化されます
TheflatMap()メソッドは、最初にStreamsの入力StreamをStringsのStreamにフラット化します(フラット化の詳細については、%(t6 )s)。 その後は、map()メソッドと同様に機能します。
4. 結論
Java 8は、関数型言語で元々使用されていたmap()およびflatMap()メソッドを使用する機会を提供します。
Streamsとオプションでそれらを呼び出すことができます。 これらのメソッドは、提供されたマッピング関数を適用することにより、マップされたオブジェクトを取得するのに役立ちます。
いつものように、この記事over on GitHubで提供されている例を確認できます。