年収アップ無料転職カウンセリング実施中! | 転職エージェントはマイナビエージェント

Geekroid-ギークロイド(仮)|ITエンジニアの日常をささいな情報で彩るコラム

ITエンジニアの転職

Streamの終端処理

終端処理では、Stream内で加工/フィルターされた値を最終的に出力/集計します。Streamでは、中間処理の時点では処理をストックします。そして、終端処理が呼び出されたところでまとめて加工/フィルターなどを実施します(遅延処理)。その性質上、中間処理は省略可能ですが、終端処理は末尾で必ず呼び出さなければなりません。

要素を順に処理する – forEachメソッド
public void forEach(Consumer<? super T> action)
T:要素の型
action:個々の要素を処理するラムダ式

たとえば以下は、Streamの内容を順に出力する例です。

StreamForEach.java

\package com.example.mynavi.streamapi;

import java.util.stream.Stream;

public class StreamForEach {
  public static void main(String[] args) {
    Stream.of("あいうえお", "かきくけこ", "さしすせそ") 
      .forEach(System.out::println);
  }
}

あいうえお
かきくけこ
さしすせそ

forEachメソッドは、「個々の要素を引数として受け取り、これを処理する(戻り値はなし)」ラムダ式を受け取ります。printlnメソッドは、引数をひとつ受け取り、戻り値がvoidなので、そのままforEachメソッドに渡せるわけです(メソッドそのものを渡す表現を、メソッド参照と呼びます)。

よって、サンプルの太字部分は以下のように書き換えても同じ意味です。

.forEach(s -> System.out.println(s));
Stream内の値が条件を満たすかを判定する – xxxxxMatchメソッド
public boolean anyMatch(Predicate<? super T> predicate)
public boolean allMatch(Predicate<? super T> predicate)
public boolean noneMatch(Predicate<? super T> predicate)
T:要素の型
predicate:条件式を表すラムダ式

anyMatchメソッドは条件式を満たす要素がひとつでもある場合、allMatchメソッドはすべての要素が条件式を満たす場合、そして、noneMatchメソッドはひとつも満たさない場合に、それぞれtrueを返します。

たとえば以下は、Stream内のすべての要素が5文字以上であるかを判定する例です。

StreamMatch.java

\package com.example.mynavi.streamapi;

import java.util.stream.Stream;

public class StreamMatch {

  public static void main(String[] args) {
    System.out.println(
        Stream.of("あかさか", "さくらぎちょう", "よよぎ")
        .★allMatch★(v -> v.length() >= 5)
    );  // 結果:false
  }
}

太字の部分をanyMatch/noneMatchに変更した場合には、それぞれ結果はtrue、falseとなります。

最初の要素だけを取得する – findFirstメソッド
public Optional<T> findFirst()
T:要素の型

findFirstメソッドを利用することで、Streamから先頭の要素だけを取得できます。

streamFirst.java

\package com.example.mynavi.streamapi;

import java.util.stream.Stream;

public class streamFirst {

  public static void main(String[] args) {
    System.out.println(
      Stream.of("あかさか", "さくま", "あおし")
        .filter(s -> s.startsWith("あ"))
        .findFirst()
        .orElse("×")
    );  // 結果:あかさか
  }
}

findFirstメソッドの戻り値は、Optional型です。よって、値を取得するにはorElseなどのメソッドを介さなければなりません。

Streamの内容を集計する
public Optional<T> max(Comparator<? super T> comparator) … 最大値
public Optional<T> min(Comparator<? super T> comparator) … 最小値
public long count() … 個数
public OptionalDouble average() … 平均値
public int sum() … 合計値
T:要素の型
comparator:比較ルールを表すラムダ式

Streamの内容を集計するために、上のようなメソッドを利用できます。ただし、average/sumメソッドが利用できるのは、IntStreamなどの基本型Streamだけです。

たとえば以下は、これらのメソッドを使って数値型Streamの内容を集計する例です。

StreamSum.java

IntStream stream =IntStream.of(15, 7, 38, 50, 1);
System.out.printf("最大値:%d\n", stream.max().orElse(0));
//System.out.printf("最小値:%d\n", stream.min().orElse(0));
//System.out.printf("個数:%d\n", stream.count());
//System.out.printf("平均値:%f\n", stream.average().orElse(0));
//System.out.printf("合計値:%d", stream.sum());

最大値:50

結果は1行ずつコメントアウト、コメントを解除した上で確認してください(一旦、終端処理を呼び出したStreamに対して繰り返し終端処理を呼び出すことはできません)。

集計値をまとめて取得したいならば、XxxxxSummaryStatisticsクラスを利用することもできます(XxxxxはInt、Long、Doubleのいずれか)。

StreamStatistics.java

\package com.example.mynavi.streamapi;

import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;

public class StreamSum {

  public static void main(String[] args) {
    IntSummaryStatistics summary =
      IntStream.of(15, 7, 38, 50, 1).collect(
          IntSummaryStatistics::new,
          IntSummaryStatistics::accept,
          IntSummaryStatistics::combine
      );

    System.out.printf("最大値:%d\n", summary.getMax());
    System.out.printf("最小値:%d\n", summary.getMin());
    System.out.printf("個数:%d\n", summary.getCount());
    System.out.printf("平均値:%f\n", summary.getAverage());
    System.out.printf("合計値:%d", summary.getSum());
  }
}

最大値:50
最小値:1
個数:5
平均値:22.200000
合計値:111

collectメソッドは、Streamの内容を束ねるための機能を提供します。独自のラムダ式を渡すこともできますが、数値集計をするならば、太字の記述はイディオムと考えてよいでしょう。

Streamの値をまとめる
public Optional<T> reduce(BinaryOperator<T> accumulator)
T:要素の型
accumulator:値を結合するためのラムダ式

reduceメソッドを利用することで、Streamの内容を独自の規則で束ねることができます。たとえば以下は、String型のStreamをタブ区切りでまとめる例です。

StreamReduce.java

\package com.example.mynavi.streamapi;

import java.util.stream.Stream;

public class StreamReduce {

  public static void main(String[] args) {
    System.out.println(
      Stream.of("あかさか", "さくらぎちょう", "よよぎ")
        .reduce((result, str) -> result + "\t" + str)
        .orElse("")
    );	// 結果:あかさか	さくらぎちょう	よよぎ
  }
}

引数accumulatorは、引数として

・演算結果を格納するための変数(result。初期値は先頭の要素)
・個々の要素を受け取る変数(str)

を受け取るラムダ式です。引数resultの内容はStreamを通じて引き継がれるので、この例であれば、Streamの内容を順に「, 要素値」で連結していくという意味になります。

Stream API
Stream APIの基本
Streamの生成
Streamによる中間処理
Streamの終端処理

年収アップ無料転職カウンセリング実施中! | 転職エージェントはマイナビエージェント

Geekroid-ギークロイド(仮)|ITエンジニアの日常をささいな情報で彩るコラム

ITエンジニアの転職