読者です 読者をやめる 読者になる 読者になる

Java8のStream APIの使い方(中間操作編② - flatMap, distinct, limit, skip)

Java
スポンサーリンク

Stream API 中間操作の flatMap、distinct、limit、skip について使い方をまとめました。

flatMap:変換 ( 1:N )

引数:Function<T, Stream<R>> / 戻り値:Stream<R>

flatMapはmapメソッドと似ていますがFunction<T, Stream<R>>を引数に取り、要素TからStream<R>を生成します。何を言ってるのかというとmapメソッドの場合は入力と出力が 1:1「(T) -> R」になりますが、flatMapメソッドの場合は 1:N「(T) -> Stream<R>」となり、Rを複数返します。0個でも大丈夫です。

flatMapの使い方

IntegerのListを各数値の数だけ出力するサンプルです。

List<Integer> list = Arrays.asList(1, 2, 3);
// ラムダ式
list.stream().flatMap(i -> Stream.generate(() -> i).limit(i)).forEach(System.out::print);

// 匿名クラス
list.stream().flatMap(new Function<Integer, Stream<Integer>>(){
    @Override
    public Stream<Integer> apply(Integer i) {
        return Stream.generate(() -> i).limit(i);
    }
}).forEach(System.out::print);

// 実行結果
122333

flatMapメソッドを最初見た時は、正直使い道が思い浮かびませんでしたが、調べてみると従来の多重ループを実現することが出来るようです。

今までのfor文で書くと以下のような形になりますが、

for(int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {
        System.out.println(i + ":" + j);
    }
}

flatMapを使用することで以下のように記述できます。しかし、これは可読性が良くないのであまり使わないかもしれません。

IntStream.range(0, 4).boxed()
    .flatMap(i -> IntStream.range(0, 4).boxed()
            .map(j -> i + ":" + j))
    .forEach(System.out::println);

defaultメソッド

関数型インターフェースがmapメソッドと同じくFunctionなので、デフォルトメソッドも同様にcompose andThen identityの3種類が使用できます。

プリミティブ型のmapメソッド

flatMapを使用してプリミティブ型のStreamに変換したい場合は、次のflatMapを使用します。

メソッド 引数 戻り値
flatMapToInt Function<T, IntStream> IntStream
flatMapToLong Function<T, LongStream> LongStream
flatMapToDouble Function<T, DoubleStream> DoubleStream

distinct:集約

引数:なし / 戻り値:Stream<T>

distinctメソッドは重複した値を除去して集約した結果のStreamを返します。重複の判定はObject#equalsで行われます。

distinctの使い方

List<Integer> lists = Arrays.asList(100, 100, 200, 200, 300);
lists.stream().distinct().forEach(System.out::print);

// 実行結果
100200300

limit : 制御

引数:long / 戻り値:Stream<T>

Streamで返す件数を指定します。

limitの使い方

String str = Stream.iterate("a", c -> c + c).limit(5).collect(Collectors.joining(", "));
System.out.println(str);

// 実行結果
a, aa, aaaa, aaaaaaaa, aaaaaaaaaaaaaaaa

skip : 読み飛ばす

引数:long / 戻り値:Stream<T>

読み込んだStreamを何件読み飛ばすか指定します。

skipの使い方

String str2 = Stream.iterate("a", c -> c + c).skip(2).limit(5).collect(Collectors.joining(", "));
System.out.println(str2);

// 実行結果
aaaa, aaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

関連記事

 Java8のforEachを使った繰り返し処理について - TASK NOTES

 Java8ラムダ式の使い方の基本 - TASK NOTES

 Java8のStream APIの使い方(Streamの生成編)

 Java8のStream APIの使い方(中間操作編① - filter, map)

 Java8のStream APIの使い方(中間操作編③ - sorted, peek)

 Java8のStream APIの使い方(終端操作編① - anyMatch, allMatch, noneMatch) - TASK NOTES