Java:Streamのreduceの使用方法
JavaのStreamにreduceという機能があるが、この使い方を解説してみる。
reduceその1
最も簡単な例
要するにストリームで得られた要素を合計するものと思えばよい。
import java.util.*;
import java.util.stream.*;
public class Sample {
public static void main(String[]args) {
// Integerのストリームを作成する
Stream<Integer>stream = Arrays.stream(new Integer[] { 1, 2, 3, 4});
// 合計を求める
int total = stream.reduce((accum, value)->accum + value).get();
// 表示 ---> total=10
System.out.println("total=" + total);
}
}
※整数の合計を出したいなら、本来はmapToIntを使用してIntStreamのsum()を使えば良いことだが、ここではあえて面倒な方法を使っている。
ここで分かりづらいのは、「(accum, value)->accum + value」というラムダ式なのだが、以下に変更してみると良くわかる。
import java.util.*;
import java.util.stream.*;
public class Sample {
public static void main(String[]args) {
// Integerのストリームを作成する
Stream<Integer>stream = Arrays.stream(new Integer[] { 1, 2, 3, 4});
// 合計を求める
int total = stream.reduce(Sample::reduce).get();
// 表示
System.out.println("" + total);
}
static int reduce(int accum, int value) {
System.out.println(accum + "," + value);
return accum + value;
}
}
この結果は以下になる。
1,2
3,3
6,4
total=10
accumのの初期値は最初の要素になっているが、次の要素、その次の要素が次々に加算されていく。
Javadocの説明
以下のように説明されている。
上記のコード例をアレンジしてわかりやすくしてみる。なお、以下の例でaccumulatorと言っているのは、上述の例の場合「 static int reduce(int accum, int value)」のことだ。
// 何らかの要素があったか
boolean foundAny = false;
// 最終結果
T result = null;
for (T element : このストリームの要素を列挙する) {
if (!foundAny) {
// 最初の要素の場合
foundAny = true;
result = element;
} else {
// 二番目以降の要素の場合
result = accumulator.apply(result, element);
}
}
// 結果があればそれを返す。なければ空を返す
return foundAny ? Optional.of(result) : Optional.empty();
複数の整数集合の和集合を求める例
こんな例が考えられるだろう。整数集合のストリームがあるので、その和集合を求める。
Stream<Set<Integer>>stream = ....;
Set<Integer>allSet = stream.reduce((total, value)-> {
total.addAll(value);
return total;
}).get();
もし、ストリーム中の各集合を変更したくないのであれば以下だ。
Stream<Set<Integer>>stream = ....
Set<Integer>allSet = stream.reduce((total, value)-> {
Set<Integer>tmp = new HashSet<Integer>(total);
tmp.addAll(value);
return tmp;
}).get();
reduceその2
先に示したreduceメソッドの他に、「T reduce(T identity, BinaryOperator
// 初期値
T result = identity;
for (T element : このストリームの要素を列挙する) {
// 初期値に加算していく
result = accumulator.apply(result, element);
}
return result;
初期値が与えられているので「値が存在しない」ということは無く、返り値はOptionalではない。
public static void main(String[]args) {
Stream<Integer>stream = Arrays.stream(new Integer[] { 1, 2, 3, 4});
int total = stream.reduce(10, (accum, value)->accum + value);
// total=20
System.out.println("total=" + total);
}