Java - Stream.collect()の使い方と例

Stream.collect() の使い方と例を紹介します。

1. Stream.collect()

collect() は、Stream のデータを変形などの処理を行い、必要なデータ型に変換してくれます。

Collectは次の機能を提供します。

  • StreamのアイテムをListまたはSetデータ型に変える
  • Streamのアイテムをjoinin
  • StreamのアイテムをSortingして最大のオブジェクト
  • ストリームのアイテムの平均値

このほか、他の多くの機能も提供しています。

Collectの基本的な機能を例と一緒に見てみましょう。

2. StreamのアイテムをHashSetに返す

Stream.collect() は、Stream の要素を別のデータ型に変換します。

Stream.collect() は以下のように 3 つの引数を受け取り、これらの引数を利用して Stream の要素を他のデータ型に変換します。

collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

以下の例は Stream.collect() を使ってストリームの結果を HashSet<String>で返されるコードです。 HashSet の Supplier、accumulator、combiner を collect() の引数として渡し、 HashSet オブジェクトが返されました。

Stream<String> fruits = Stream.of("banana", "apple", "mango", "kiwi", "peach", "cherry", "lemon");
HashSet<String> fruitHashSet = fruits.collect(HashSet::new, HashSet::add, HashSet::addAll);
for (String s : fruitHashSet) {
    System.out.println(s);
}

Output:

banana
apple
cherry
kiwi
lemon
peach
mango

2.1 Collectorsを使用した同じコンテンツの実装

上記のコードは collect に 3 つの params を入れなければならず、やや使いにくいです。 Collectorsというライブラリが基本的な機能を提供し、このような不便さを解決します。

Collectors で collect で HashSet オブジェクトを作成する例を次に示します。引数に Collectors.toSet() を入れてくれれば Set データ型にしてくれます。

Stream<String> fruits = Stream.of("banana", "apple", "mango", "kiwi", "peach", "cherry", "lemon");
Set<String> fruitSet = fruits.collect(Collectors.toSet());
for (String s : fruitSet) {
    System.out.println(s);
}

Output:

banana
apple
cherry
kiwi
lemon
peach
mango

3. Streamの要素をListに変換

Collectors を使用すると、ストリームの要素を List オブジェクトに変換できます。

以下のように Collectors.toList() を引数に渡すと、List オブジェクトとして返されます。

Stream<String> fruits = Stream.of("banana", "apple", "mango", "kiwi", "peach", "cherry", "lemon");
List<String> fruitList = fruits.collect(Collectors.toList());
for (String s : fruitList) {
    System.out.println(s);
}

Output:

banana
apple
mango
kiwi
peach
cherry
lemon

4. Stream要素を1つのStringオブジェクトに変換

Streamのすべての要素を1つのStringにすることもできます。

以下のように Collectors.joining() を引数として入れてください。

Stream<String> fruits = Stream.of("banana", "apple", "mango", "kiwi", "peach", "cherry", "lemon");
String result2 = fruits.collect(Collectors.joining());
System.out.println(result2);

Output:

bananaapplemangokiwipeachcherrylemon

文字列を合わせるときに区切り文字を入れたい場合は、 Collectors.joining(", ") のように引数として入れてください。

Stream<String> fruits = Stream.of("banana", "apple", "mango", "kiwi", "peach", "cherry", "lemon");
String result2 = fruits.map(Object::toString).collect(Collectors.joining(", "));
System.out.println(result2);

Output:

banana, apple, mango, kiwi, peach, cherry, lemon

5. 最大のオブジェクトを 1 つだけ返す

ストリームのアイテムを任意の条件で比較して満たされた1つのアイテムだけを返すことができます。

次のコードは、文字列の長さが最も長い要素を返す例です。 Optional<String>に戻り、文字列の長さを比較する関数(Comparator)は直接作成して引数に渡しました。

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.maxBy;

Stream<String> fruits = Stream.of("banana", "apple", "mango", "kiwi", "peach", "cherry", "lemon");
Function<String, Integer> getCount = fruit-> fruit.length();
Optional<String> result = fruits.map(Object::toString).collect(maxBy(comparing(getCount)));
System.out.println("result: " + result.orElse("no item"));

Output:

result: banana

6. Collectorsで平均値を取得する

Collectorsは、ストリームアイテムの平均値を取得する機能も提供します。

List<Integer> list = Arrays.asList(1,2,3,4);
Double result = list.stream().collect(Collectors.averagingInt(v -> v*2));
System.out.println("Average: "+result);

Output:

Average: 5.0

7. Custom オブジェクトへの Collect の適用

Custom オブジェクトに対しても Collect を行うこともできます。 Collectors.toMap() を使うだけです。

Stream<Fruit> fruits2 = Stream.of(new Fruit("1", "banana"), new Fruit("2", "apple"),
        new Fruit("3", "mango"), new Fruit("4", "kiwi"),
        new Fruit("5", "peach"), new Fruit("6", "cherry"),
        new Fruit("7", "lemon"));
Map<String, String> map = fruits2.collect(Collectors.toMap(Fruit::getId, Fruit::getName));
for (String key : map.keySet()) {
    System.out.println("key: " + key + ", value: " + map.get(key));
}

static class Fruit {
    public String id;
    public String name;

    Fruit(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }
    public String getName() {
        return name;
    }
}

Output:

key: 1, value: banana
key: 2, value: apple
key: 3, value: mango
key: 4, value: kiwi
key: 5, value: peach
key: 6, value: cherry
key: 7, value: lemon

8. toMap() 実行時、同じ Key に対する例外処理

toMap() 実行中、同じキーを持っているデータに会うと IllegalStateException を発生させます。 この場合、例外処理を行うことができます。 3番目のparamでは、存在する値と新しい値がどの値を格納するかを定義する必要があります。 以下のコードは、既存の登録値を使用するために (existingValue, newValue) -> existingValue) として定義しました。 (コードを見るとキー5を持つデータが2つあります。)

Stream<Fruit> fruits2 = Stream.of(new Fruit("1", "banana"), new Fruit("2", "apple"),
        new Fruit("3", "mango"), new Fruit("4", "kiwi"),
        new Fruit("5", "peach"), new Fruit("6", "cherry"),
        new Fruit("5", "lemon"));
Map<String, String> map = fruits2.collect(
    Collectors.toMap(item -> item.getId(), item -> item.getName(),
            (existingValue, newValue) -> existingValue));
for (String key : map.keySet()) {
    System.out.println("key: " + key + ", value: " + map.get(key));
}

キー5の場合、peachとlemonがあり、peachが最初に登録されているので、peachがmapに保存されます。

key: 1, value: banana
key: 2, value: apple
key: 3, value: mango
key: 4, value: kiwi
key: 5, value: peach
key: 6, value: cherry

今回は、同じキーを持つデータがあるときに、2つの値を合計して保存するように定義できます。 3番目のparamで、2つの値を加えた値を返す関数を定義するだけです。

Stream<Fruit> fruits2 = Stream.of(new Fruit("1", "banana"), new Fruit("2", "apple"),
        new Fruit("3", "mango"), new Fruit("4", "kiwi"),
        new Fruit("5", "peach"), new Fruit("6", "cherry"),
        new Fruit("5", "lemon"));
Map<String, String> map = fruits2.collect(
        Collectors.toMap(item -> item.getId(), item -> item.getName(),
                (existingValue, newValue) -> {
                    String concat = existingValue + ", " + newValue;
                    return concat;
                }));
for (String key : map.keySet()) {
    System.out.println("key: " + key + ", value: " + map.get(key));
}

キー 5 の値を見ると、2 つの文字列を合わせた文字列が保存されました。

key: 1, value: banana
key: 2, value: apple
key: 3, value: mango
key: 4, value: kiwi
key: 5, value: peach, lemon
key: 6, value: cherry

Related Posts

codechachaCopyright ©2019 codechacha