カスタムサプライヤを使用してリストをマップに変換する
1. 概要
このチュートリアルでは、 リスト
2. JDK8のサプライヤー
サプライヤーは工場としてよく使われます。 メソッドはSupplierを入力として受け取り、制限付きワイルドカードタイプを使用してタイプを制約できます。その後、クライアントは、指定されたタイプの任意のサブタイプを作成するファクトリを渡すことができます。
その上、サプライヤーは怠惰な値の生成を実行できます。
3. リストをマップに変換する
Stream APIは、List操作のサポートを提供します。 そのような例の1つは、Stream#collectメソッドです。 ただし、Stream APIメソッドには、Supplierをダウンストリームパラメーターに直接与える方法はありません。
このチュートリアルでは、 Collectors.groupingBy 、 Collectors.toMap 、およびStream.collectメソッドとサンプルコードスニペットを見ていきます。 カスタムサプライヤーを使用できるようにする方法に焦点を当てます。
このチュートリアルでは、次の例で文字列リストコレクションを処理します。
List source = Arrays.asList("List", "Map", "Set", "Tree");
上記のリストを、文字列の長さをキーとするマップに集約します。 完了すると、次のようなマップが作成されます。
{
3: ["Map", "Set"],
4: ["List", "Tree"]
}
3.1. Collectors.groupingBy()
Collectors.groupingBy を使用すると、Collectionを特定の分類子を使用してMapに変換できます。 分類子は要素の属性です。この属性を使用して、要素をさまざまなグループに組み込みます。
public Map<Integer, List> groupingByStringLength(List source,
Supplier<Map<Integer, List>> mapSupplier,
Supplier<List> listSupplier) {
return source.stream()
.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toCollection(listSupplier)));
}
それが機能することを検証できます:
Map<Integer, List> convertedMap = converter.groupingByStringLength(source, HashMap::new, ArrayList::new);
assertTrue(convertedMap.get(3).contains("Map"));
3.2. Collectors.toMap()
Collectors.toMap メソッドは、ストリーム内の要素をMap。に縮小します。
まず、ソース文字列とListおよびMapサプライヤーの両方を使用してメソッドを定義します。
public Map<Integer, List> collectorToMapByStringLength(List source,
Supplier<Map<Integer, List>> mapSupplier,
Supplier<List> listSupplier)
次に、要素からキーと値を取得する方法を定義します。 そのために、2つの新しい機能を利用します。
Function<String, Integer> keyMapper = String::length;
Function<String, List> valueMapper = (element) -> {
List collection = listSupplier.get();
collection.add(element);
return collection;
};
最後に、キーの競合時に呼び出される関数を定義します。 この場合、両方の内容を組み合わせる必要があります。
BinaryOperator<List> mergeFunction = (existing, replacement) -> {
existing.addAll(replacement);
return existing;
};
すべてをまとめると、次のようになります。
source.stream().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier))
ほとんどの場合、定義する関数は、メソッドの引数リスト内の匿名のインライン関数であることに注意してください。
それをテストしてみましょう:
Map<Integer, List> convertedMap = converter.collectorToMapByStringLength(source, HashMap::new, ArrayList::new);
assertTrue(convertedMap.get(3).contains("Map"));
3.3. Stream.collect()
Stream.collect メソッドを使用して、ストリーム内の要素を任意のタイプのコレクションに減らすことができます。
そのためには、ListとMapの両方のサプライヤーを使用して、新しいコレクションが必要になったときに呼び出されるメソッドを定義する必要もあります。
public Map<Integer, List> streamCollectByStringLength(List source,
Supplier<Map<Integer, List>> mapSupplier,
Supplier<List> listSupplier)
次に、アキュムレータを定義します。このアキュムレータは、要素のキーを指定すると、既存のリストを取得するか、新しいリストを作成して、応答に要素を追加します。
BiConsumer<Map<Integer, List>, String> accumulator = (response, element) -> {
Integer key = element.length();
List values = response.getOrDefault(key, listSupplier.get());
values.add(element);
response.put(key, values);
};
最後に、アキュムレータ関数によって生成された値を結合するように移動します。
BiConsumer<Map<Integer, List>, Map<Integer, List>> combiner = (res1, res2) -> {
res1.putAll(res2);
};
すべてをまとめて、要素のストリームでcollectメソッドを呼び出すだけです。
source.stream().collect(mapSupplier, accumulator, combiner);
ほとんどの場合、定義する関数は、メソッドの引数リスト内の匿名のインライン関数であることに注意してください。
テスト結果は、他の2つの方法と同じになります。
Map<Integer, List> convertedMap = converter.streamCollectByStringLength(source, HashMap::new, ArrayList::new);
assertTrue(convertedMap.get(3).contains("Map"));
4. 結論
このチュートリアルでは、変換する方法を説明しましたリスト
このチュートリアルの例を含む完全なソースコードは、GitHubのにあります。