#ストリーム終端処理
- ストリーム処理で加工/フィルタされた値を最終的に出力したり集計する
- ストリームは終端処理をトリガーにまとめて処理される
- 終端処理は省略不可
- 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
}
}