2
7

More than 3 years have passed since last update.

【Java】Stream API - ストリーム終端処理

Posted at

ストリーム終端処理

  • ストリーム処理で加工/フィルタされた値を最終的に出力したり集計する
  • ストリームは終端処理をトリガーにまとめて処理される
  • 終端処理は省略不可
    • cf:中間処理は省略可能
  • 終端処理後にストリーム再利用は不可能(IllegalStateException)

要素を順に処理

  • forEachメソッド
  • System.out::printlnメソッド参照をそのまま渡す以外にラムダ式で渡すこともできる
import java.util.stream.Stream;
public class StreamForEach {
  public static void main(String[] args) {
    Stream.of("Munchkin", "Siamese", "Persian", "Tama")
    .forEach(v -> System.out.println(v));
  }
}
  • forEachメソッドは並列ストリームでは順序を保証しない!
  • →順番を守るにはforEachOrderedメソッドを使う!
import java.util.stream.Stream;
public class Main {
  public static void main(String[] args) {
    Stream.of("Munchkin", "Siamese", "Persian", "Tama")
    .parallel()
    .forEach(v -> System.out.println(v));
    //Persian Tama Siamese Munchkin 

    /*順番を守りたい場合
    .forEachOrdered(v -> System.out.println(v)); 
    Munchkin Siamese Persian Tama
    */
  }
}

最初の値を取得する

  • findFirstメソッド
  • ストリームがからである可能性もあるので、戻り値はOptional型
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {

    var str = Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
        .filter(s -> s.startsWith("S"))
        .findFirst();
      //orElseメソッドでnullの場合"-"に置き換える
      System.out.println(str.orElse("-")); //Siamese
  }
}
  • findAnyメソッドは並列ストリームで得られた最初の結果を返す*ので結果が変わることも
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {

    var str = Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
    .parallel()
    .filter(s -> s.startsWith("S"))
    .findAny();
     System.out.println(str.orElse("-")); //Scottish Fold
  }
}

値が特定の条件を満たすか判定

  • anyMatch/allMatch/noneMatchメソッド
//リスト内の値が全部0以上か確認
import java.util.stream.IntStream;

public class StreamMatch {

  public static void main(String[] args) {
    System.out.println(
        IntStream.of(1, 10, 5, -5, 12)
          .allMatch(v -> v >= 0)
      ); //false
  }
}

配列/コレクションに変換

  • toArrayメソッド
  • ストリーム処理結果を文字列配列として取得
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {
    var list = Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
        .filter(s -> s.startsWith("S"))
        .toArray();
    System.out.println(list[0]); //Siamese
  }
}
  • collectメソッドでコレクションに変換
  • ストリーム処理結果をリストに変換
    • toList:リストへの変換
    • toSet:セットへの変換
    • toMap:マップへの変換
    • toCollection:一般的なコレクションへの変換
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTrans2 {

  public static void main(String[] args) {
    var list = Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
        .filter(s -> s.startsWith("S"))
        .collect(Collectors.toList());
    System.out.println(list);
  }
}
  • toMapメソッド
    • 重複する可能性がある場合、merge引数を設定すること(IllegalStateException)
//Personオブジェクト配列を名前:メールアドレスのマップ形式に変換
public class StreamCollectMap {

  public static void main(String[] args) {
    System.out.println(
        Stream.of(
          new Person("山田太郎", "tyamada@example.com"),
          new Person("鈴木花子", "hsuzuki@example.com"),
          new Person("井上三郎", "sinoue@example.com"),
          new Person("佐藤久美", "ksatou2@example.com"),
          new Person("山田太郎", "yamataro@example.com")
        ).collect(Collectors.toMap(
          //引数keyがマップのキーを表す
          Person::getName,
          //引数valueがマップのキーを表す
          Person::getEmail,
          //重複する可能性がある場合
          (s, a) -> s + "/" + a
          //重複時に値を上書き
          // (s, a) ->  a
        ))
      );
  }
}
public class Person {
  private String name;
  private String email;
  public Person(String name, String email) {
    this.name = name;
    this.email = email;
  }
  public String getName() {
    return name;
  }
  public String getEmail() {
    return email;
  }
}

最大、最小を求める

  • min.maxメソッド
  • 引数にComparator型
  • naturalOrderで自然順ソート
  • 戻り値はOptional型なので、値取り出しにはorElseメソッド
import java.util.Comparator;
import java.util.stream.Stream;

public class StreamMin {

  public static void main(String[] args) {
    var str = Stream.of("めばる", "さんま", "ひらめ", "いわし", "ほっけ")
        .min(Comparator.naturalOrder());
      System.out.println(str.orElse("")); //いわし
  }
}

要素個数を求める

  • countメソッド
//文字列長3より大きい文字列の個数を求める
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {
    System.out.println(
    Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
      .filter(s -> s.length() > 7)
      .count() //2
    );
  }
}

合計/平均値を求める

  • sum/averageメソッド
  • 基本型ストリームでのみ使用可能
  • averageメソッドの戻り値はOptionalDouble型
    • orElseメソッドでとり出す
//int配列合計、平均を求める
import java.util.stream.IntStream;

public class StreamSum {

  public static void main(String[] args) {
    var list = new int[] { 5, 1, 10, -3 };
    System.out.println(IntStream.of(list).sum()); //13
    System.out.println(IntStream.of(list).average().orElse(0)); //3.25
  }
}

ストリームの値を一個にまとめる

  • リダクション
    • max/min/countメソッドもリダクション
  • reduceメソッドでリダクションを汎用的に行う

引数1個の場合:

  • reduceメソッド(ラムダ式)が受け取る引数
    • 演算結果を格納するための変数(result)
    • 個々の要素を受け取るための変数(str)
//文字列ストリームをカンマで区切りでまとめる
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {

    System.out.println(
      Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
        .sorted()
        .reduce((result, str) -> {
          return result + "," + str;
        })
        .orElse("") //Munchkin,Persian,Scottish Fold,Siamese,Tama
    );
  }
}

引数2個の場合:

  • 初期値を受け取ることができる
  • 結果は非nullであるので非Optional
    • orElse経由なしでも値取得可能
  • 並列ストリームでは分散された分、初期値がラムダ式に適用されるので結果が変化
    • 並列化した分重複する初期値が可能性がある
import java.util.stream.Stream;
public class StreamReduce2 {
  public static void main(String[] args) {
    System.out.println(
      Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
        .sorted()
        // .parallel()
        .reduce("Himalayan", (result, str) -> {
          return result + "," + str;
        })
    ); //Himalayan,Munchkin,Persian,Scottish Fold,Siamese,Tama
  }
}

引数3個の場合:

  • Stream要素型と最終結果型が異なる場合に使用
  • reduceで個々の要素を取得する際に演算結果(result)、個々の要素の変数(value)が異なる型でもOK
    • 引数result(Integer)に文字列valueを数値に変換したものを加算
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {
    System.out.println(
      Stream.of("153", "211", "112", "350", "418", "208")
        .parallel()
        .reduce(0,
          //個々の要素を取得
          //演算結果をresultに格納、個々の要素をvalueが受け取る
          (result, value) -> {
            return result + Integer.parseInt(value); //文字列valueを数値に変換
          },
          //分散された結果をまとめる(並列ストリームのみ)
          (result1, result2) -> {
            return result1 + result2; //1452
          }
      )
    );
  }
}

ストリーム要素をコレクションにまとめる(1)

  • collectメソッド
  • 可変リダクション:コレクションやStringBuilderのように可変なコンテナ(入れ物)に値を蓄積して返す
    • cf: reduceはストリーム内要素をint/Stringのような単一型にまとめる
//与えられた文字列ストリームをソートしてリストに変換
import java.util.ArrayList;
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {
    System.out.println(
      Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama"
        .sorted()
        .collect(
          //式数でリダクション最初の値に値を格納するコンテナ生成
          ArrayList<String>::new,
          //引数はコンテナ、個々の要素
          (list, str) -> list.add(str),
          //並列ストリームの場合
          (list1, list2) -> list1.addAll(list2)
        )
    );
  }
}

ストリーム要素をコレクションにまとめる(2)

  • collectメソッドはオーバーロードを持つ
  • Collector.of静的メソッドで生成できる
  • コレクター属性
    • CONCURRENT:マルチスレッドでaccumulator実行可能
    • UNORDERED:要素の順序を保証しない
    • IDENTITY_FINISH:finisherを省略可能
import java.util.ArrayList;
//import java.util.Arrays;
import java.util.stream.Collector;
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {
    System.out.println(
      Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
        .sorted()
        .collect(
          Collector.of(
            ArrayList::new,
            ArrayList::add,
            (list1, list2) -> {
              list1.addAll(list2);
              return list1;
            },
            Collector.Characteristics.IDENTITY_FINISH
          )
        )
    );
  }
}

標準コレクター

  • Collectorsクラスにcollectメソッドで利用できるコレクターを生成するファクトリーメソッドがある
  • 典型的なリダックション処理を簡単に実装!
    • toList、toMapメソッドもファクトリーメソッドに含まれる

joiningメソッド

  • 文字列結合のコレクター生成
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

  public static void main(String[] args) {
    System.out.println(
        Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
          .sorted()
          .collect(Collectors.joining(",", "<", ">")) //<Munchkin,Persian,Scottish Fold,Siamese,Tama>
      );
  }
}

groupingByメソッド

  • 指定キーで値をグループ化するコレクター生成
//文字列を長さ別に分類
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

  public static void main(String[] args) {
    System.out.println(
        Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
          .sorted()
          .collect(
          //引数strで要素型を受け取りグループ化キーを生成
          //文字列単位でグループ化
            Collectors.groupingBy(str -> str.length()
          )) //{4=[Tama], 7=[Persian, Siamese], 8=[Munchkin], 13=[Scottish Fold]}
      );
  }
}
  • グループ化した結果を別のコレクターで処理
    • joiningに引数指定
public class Main {

  public static void main(String[] args) {

    System.out.println(
        Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
          .sorted()
          .collect(
            Collectors.groupingBy(
              str -> str.length(),
              Collectors.joining("/") //{4=Tama, 7=Persian/Siamese, 8=Munchkin, 13=Scottish Fold}
          ))
      );
  }
}

partitioningByメソッド

  • true/falseに分割するシンプルなグループ化
//文字列が7文字以内か長いかで分ける
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

  public static void main(String[] args) {

    System.out.println(
        Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
          .sorted()
          .collect(
            Collectors.partitioningBy(
              str -> str.length() > 7
          ) //{false=[Persian, Siamese, Tama], true=[Munchkin, Scottish Fold]}
        )
      );
  }
}

collectingAndThenメソッド

  • コレクター実行後に終了処理実行
  • コレクターと後処理を連結
//toListメソッドでストリームをリスト化
//Collections::unmodifiableListで読み取り専用に変換

import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

  public static void main(String[] args) {
    System.out.println(
        Stream.of("Munchkin", "Siamese", "Persian", "Scottish Fold", "Tama")
          .sorted()
          .collect(
            Collectors.collectingAndThen(
              Collectors.toList(),
              Collections::unmodifiableList
            )
          )
      );
  }
}

IntSummaryStatisticsクラス

  • 数値の基本的な統計情報を取得
  • collectメソッドの戻り値は(Int)SummaryStatisticsオブジェクト
  • ゲッターメソッドで統計情報取得
    • getAverage():平均値(値がない場合0)
    • getCount():要素個数
    • getMax():最大
    • getMin():最小
    • getSum():合計(値がない場合0)
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;

public class Main {

  public static void main(String[] args) {
    var summary = IntStream.of(5, 13, 7, 2, 30)
      .collect(
     //コンストラクタ、accept、combineDBをparseIntソッドの参照を渡す
        IntSummaryStatistics::new,
        IntSummaryStatistics::accept,
        IntSummaryStatistics::combine
      );
    System.out.println(summary.getMin()); //2
    System.out.println(summary.getSum()); //57
    System.out.println(summary.getAverage()); //11.4
  }
}
2
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7