当サイトは、アフィリエイト広告を利用しています

【java8】streamのreduceの使い方

作成日:2022月03月15日
更新日:2023年12月14日

javaのstreamAPIのreduceを使いながら整理してみる。

下記の本では streamAPI についても詳しく書いてありおススメです!

また当ブログで紹介しているjava8のstreamAPIを使った
コレクションや配列の操作方法を
下記記事でメソッド別にまとめています!

java8のreduce

java8のreduceにはパラメータが1個、2個、3個のversionと三つある

【パラメータが1つのメソッド】

まずはパラメータが一つのメソッド。
戻り値がオプショナル型となるため注意が必要。

基本構文

戻り値をオプショナル型で帰ってくるため、後処理が必要。
パラメータとしては二つの引数を与えて、一つの引数と同じ型の結果を返す関数型インターフェースの
BinaryOperator型の関数を渡す。
BinaryOperatorは引数、戻り値の型を一つしか設定できない。

java
Optional<T> reduce(BinaryOperator<T> accumulator);

実装例

java
public static void main(final String[] args) {
final List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
final Optional<String> result = list.stream().reduce(
// BinaryOperator型の関数を定義
(accum, value) -> {
return accum + "-" + value;
});
//結果はoptional
System.out.println(result.orElse(""));
}
// 実行結果
// one-two-three

BinaryOperatorのオブジェクト(関数)がパラメータになる 内部では途中結果を保持するためのアキュムレータと 要素の値をパラメータとして受け取る、BinaryOperatorのオブジェクト(関数)が生成される。
reduceのパラメータに関数を渡して処理させるイメージ。(高階関数) 最初にreduceが実行される段階ではaccumが"one"、 valueが"two"で実行され 2回目はaccumが"one-two"、valueが"three"で実行される。

【パラメータが2つのメソッド】

次にパラメータが2つのメソッド。
初期値を設定できるため一番使う気がする。

基本構文

第一引数(identity)で初期値を設定するため、戻り値は
初期値の型となる。
第二引数にはパラメータが一つの時と同様にBinaryOperator型の関数を設定する。

java
T reduce(T identity, BinaryOperator<T> accumulator);

実装例

java
public static void main(final String[] args) {
final List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
// // BinaryOperator型の関数を定義
final BinaryOperator<String> operation =
(accum, value) -> {
return accum + "-" + value;
};
// 中身のあるListに対して実行
final String result1 = list.stream().reduce("value", operation);
// 空Listに対して実行
final String result2 = new ArrayList<String>().stream().reduce("value", operation);
System.out.println(result1);
System.out.println(result2);
}
// 実行結果
// value-one-two-three
// value

パラメータが2つの場合はStream内要素と同じ型のオブジェクトと
BinaryOperatorのオブジェクトがパラメータなります

パラメータが2つの場合は1つめのパラメータに渡した値が
アキュムレータの初期値になる
※パラメータが1つの場合は自動的に最初の要素がアキュムレータの初期値となっていた
2つめのパラメータは実際に処理を行うBinaryOperator型の関数を設定する
戻り値の方はStream内のオブジェクトと同じ型が返却される
第一引数(初期値)に明示的にnullを渡した場合はヌルぽが発生する。

参考

StreamAPIについて調べてみた reduce編 その1

【パラメータが3つのメソッド】

パラメータ3つのメソッドの場合は、戻り値に要素と違う型を返すことができる。
※上記二つは用途と同じ型しか返せない

基本構文

第一引数(identity)は初期値。
第二引数には二つの引数で指定した型で戻り値を返すBiFunction型の関数を設定する。
第三引数にはBinaryOperator型の関数の設定する。

java
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);

実装例

文字列のリストからリスト内のlenthを合計した値を返す。
引数が3つ以外では文字列結合した値しか返せない

java
import java.util.*;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) throws Exception {
List<String> fruits = Arrays.asList("apple", "orange", "kiwi");
int result = fruits.stream().reduce(
0, // 初期値
(sum, elm) -> sum + elm.length(), // accumulator. 中間生成物を作る.
(sum1, sum2) -> sum1 + sum2); // combiner. 中間生成物どうしをマージする.
// 結果を出力
System.out.println(result);
}
}
// 結果
// 15

reduceで別の型で返す

実際に使ってみた

下記では実際に使ってみたサンプルをいくつか乗せる。

オブジェクトリストのプロパティの合計を計算する場合(BigDecimalVer)

オブジェクトのBigDecimal型のプロパティをreduceで合計する場合
第二引数はメソッド参照を使うことができる。
またオブジェクトリストに対してreduceを使う場合はmapで
算出対象プロパティだけを抽出する。

java
import java.util.*;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) throws Exception {
// 、
class Person {
private final String name;
private final BigDecimal weight;
Person(String name, BigDecimal weight) {
this.name = name;
this.weight = weight;
}
public BigDecimal getWeight() {
return this.weight;
}
}
Person p1 = new Person("太郎", new BigDecimal("72.1"));
Person p2 = new Person("二郎", new BigDecimal("79.1"));
List<Person> personList = Arrays.asList(p1, p2);
BigDecimal totalWeight = personList.stream()
//メソッド参照でWeightだけ取得
.map(Person::getWeight)
// Weightだけrecudeで集計
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(totalWeight);
}
}

BigDecimal型の使い方については下記の記事で紹介しています。

参考

streamを使ってBigDecimalの計算をする - sum編 -

通常のオブジェクトリストの場合

集計を出したいプロパティだけリストをmapで取り出してからrecudeする
reduceの第二引数には関数を設定する

java
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
class Person {
private final String name;
private final int weight;
Person(String name, int weight) {
this.name = name;
this.weight = weight;
}
public int getWeight() {
return this.weight;
}
}
Person p1 = new Person("太郎", 72);
Person p2 = new Person("二郎", 79);
List<Person> personList = Arrays.asList(p1, p2);
int totalWeight = personList.stream()
//メソッド参照でWeightだけ取得
.map(Person::getWeight)
// Weightだけrecudeで集計
.reduce(0, (accum,value)->accum + value);
System.out.println(totalWeight);
}
}

配列の場合

配列の場合などは基本構文通り、第二引数には関数を設定する。

java
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);
}

参考

Guide to Stream.reduce()

新着記事

タグ別一覧
top