Streamにおけるmapとflatmapの理解へ向けて(1)


問い

2つのIntegerのリストから、
互いの全ての要素の組み合わせから成るPairのリストを生成せよ。

ex.) [1,2,3], [5,6] -> [(1,5),(1,6),(2,5),(2,6),(3,5),(3.6)]

trial1

なんかmapでいけるかも。

List<Integer> ints1 = Arrays.asList(1,2,3);
List<Integer> ints2 = Arrays.asList(5,6);
ints1.stream()
     .map( i -> ints2.stream().map( j -> Pair.of(i,j)))
     .collect(toList()); 

だめ。
List<Stream<Pair<Integer, Integer>>が返される。

そもそも...
Stream.map関数は、型Function<? super T,? extends R>を引数にとり、<R> Stream<R>を返す。

Stream map(Function<? super T,? extends R> mapper)
ref. Stream.map

なので、入れ子となっているmapは、
Stream<Pair<Integer, Integer>>を返し、
その戻り値を受けた大元のmapが、
Stream<Stream<Pair<Integer, Integer>>>を返すようになる。

そして、終端操作により、
List<Stream<Pair<Integer, Integer>>が最終的に返される。
(終端操作がどのような処理をしているかについては今後...)

そこでflatMap

flatMapを使うことで、
Streamの各値を別のStreamで置き換え、
生成された全てのStreamは、単一のStreamへ集約されるようになる。
ref. Java8 in Action

定義によると、
flatMapへ引数で渡している
Function<? super T,? extends Stream<? extends R>> mapperの第二引数が、
? extends Stream<? extends R>となっており、
戻り値は、
Functionの第二引数のStreamを構成する型Rの<R> Stream<R>となっている。

Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
ref. flatMap

以上を踏まえると、以下のようなコードとなる。

ints1.stream()
     .flatMap( i -> ints2.stream().map( j -> Pair.of(i,j)))
     .collect(toList());

flatMap( i -> ints2.stream().map( j -> Pair.of(i,j)))
によって返される型は、
Stream<Pair<Integer,Intege>>で、
終端操作により、List<Pair<Integer,Integer>>が返される。

flatMapを使うことで、
trial1で入れ子となっていたStreamをよしなにしてくれる感じになる ^^;(説明雑)