lambdaでListをMapに変換する
List<Beans>をList<Beansの一部>にlambdaを使って一文で変換するの続きとして、Java 8で導入されるlambdaを使って簡潔に、List<Beans>
からMap
に変換する。
Collectors.toMapを使う
Collectors.toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
でKey, Valueをそれぞれ指定できる。
import static java.util.function.Function.identity;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* List<Beans>をMapにLambdaを使って一行で変換する
*/
public class Main {
public static void main(String[] args) {
Main m = new Main();
// DTOを取得する
List<Dto> dtos = m.getDtos();
// ListからMapへの変換
Map<String, Dto> map = dtos.stream()
.collect(Collectors.toMap(Dto::getKey, identity()));
// 上のDto::getKey, identity()はdto -> dto.getKey(), dto -> dtoと同じ意味で、次のようにも書ける
Map<String, Dto> map_ = dtos.stream()
.collect(Collectors.toMap(dto -> dto.getKey(), dto -> dto));
// forEachで一つずつ取り出して表示
map.forEach((key, val) -> System.out.println("key:" + key + " val:" + val));
}
/**
* DBからデータを取り出すモック
* @return DTO
*/
private List<Dto> getDtos() {
List<Dto> dtos = Arrays.asList(
new Dto("key1", "x1")
, new Dto("key2", "x2")
);
return dtos;
}
}
class Dto {
public Dto(String key, String x) {
this.key = key;
this.x = x;
}
String key;
String x;
public String getKey() {
return key;
}
public String getX() {
return x;
}
@Override
public String toString() {
return "Dto [key=" + key + ", x=" + x + "]";
}
}
MapのKeyが重複するとき
Listは重複した値を持っている可能性がある。MapのKeyが重複するときの対処方法を考える必要がある。
Collectors.toMapのオーバーロードメソッドを利用する
もしKeyがかぶると、Collectors.toMapでIllegalStateException
が起きてしまう。その場合は、オーバロードされたtoMapを使用する。
// 型パラメータ:
// T - 入力要素の型
// K - キー・マッピング関数の出力の型
// U - 値マッピング関数の出力の型
// パラメータ:
// keyMapper - キーを生成するマッピング関数
// valueMapper - 値を生成するマッピング関数
// mergeFunction - 同じキーに関連付けられた値同士の衝突の解決に使用されるマージ関数(Map.merge(Object, Object, BiFunction)に渡される)
Collectors.toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
以下のような使い方になる。
public static void main(String[] args) {
Main m = new Main();
// DTOを取得する
List<Dto> dtos = new ArrayList<>();
// keyが重複するデータを作成する
dtos.addAll(m.getDtos());
dtos.addAll(m.getDtos());
// ListからMapへの変換
Map<String, String> map = dtos.stream()
.collect(Collectors.toMap(
Dto::getKey
, Dto::getX
, (v1, v2) -> String.join(",", v1, v2));
// forEachで一つずつ取り出して表示
map.forEach((key, val) -> System.out.println("key:" + key + " val:" + val));
}
集計関数を利用する
同じことは集計関数を使用しても実現できる。
集計関数を使用するとCollectorsクラスを大量に使うことがあり、staticインポートした方がコードが綺麗になる。
import static java.util.stream.Collectors.*;
public static void main(String[] args) {
// 略
// ValueがList
Map<String, List<Dto>> map = dtos.stream()
.collect(groupingBy(
Dto::getKey));
// Valueを独自に計算
Map<String, String> map_ = dtos.stream()
.collect(groupingBy(
Dto::getKey
, mapping(Dto::getX, joining(","))));
}