Java 8で2つのマップをマージする
1前書き
このクイックチュートリアルでは、Java 8の機能を使って2つのマップをマージする方法を説明します。
より具体的には、重複するエントリを持つマップなど、さまざまなマージシナリオを検討します。
2初期化
はじめに、2つの
Map
インスタンスを定義しましょう。
private static Map<String, Employee> map1 = new HashMap<>();
private static Map<String, Employee> map2 = new HashMap<>();
Employee
クラスは次のようになります。
public class Employee {
private Long id;
private String name;
//constructor, getters, setters
}
その後、
Map
インスタンスにデータをプッシュします。
Employee employee1 = new Employee(1L, "Henry");
map1.put(employee1.getName(), employee1);
Employee employee2 = new Employee(22L, "Annie");
map1.put(employee2.getName(), employee2);
Employee employee3 = new Employee(8L, "John");
map1.put(employee3.getName(), employee3);
Employee employee4 = new Employee(2L, "George");
map2.put(employee4.getName(), employee4);
Employee employee5 = new Employee(3L, "Henry");
map2.put(employee5.getName(), employee5);
後で使用するマップでは、
employee1
エントリと
employee5
エントリに同じキーがあります。
3
Map.merge()
-
Java 8は
java.util.Map
インターフェースに新しい
merge()
関数を追加しました** 。
これは、
merge()
関数がどのように機能するかを示したものです。指定されたキーがまだ値に関連付けられていない場合、または値がnullの場合は、キーを指定の値に関連付けます。
そうでなければ、値を与えられたリマッピング関数の結果で置き換えます。再マッピング関数の結果がNULLの場合は、結果が削除されます。
まず、
map1
からすべてのエントリをコピーして、新しい
HashMap
を作成しましょう。
Map<String, Employee> map3 = new HashMap<>(map1);
次に、マージ規則とともに
merge()
関数を紹介しましょう。
map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())
最後に、
map2
を繰り返し処理して、エントリを
map3
にマージします。
map2.forEach(
(key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())));
プログラムを実行して
map3
の内容を印刷しましょう。
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
Henry=Employee{id=1, name='Henry'}
その結果、
結合された
Map
は、前の
HashMap
エントリのすべての要素を持ちます。重複キーを持つエントリは1つのエントリにマージされました
。
また、最後のエントリの
Employee
オブジェクトには
map1
の
id
があり、値は
map2
から選択されています。
これは、マージ関数で定義した規則によるものです。
(v1, v2) -> new Employee(v1.getId(), v2.getName())
4
Stream.concat()
Java 8の
Stream
APIは、私たちの問題に対する簡単な解決策を提供することもできます。まず、
Map
インスタンスを1つの
Stream
に結合する必要があります。
Stream.concat()
操作の動作はまさにそれです。
Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());
ここではマップエントリセットをパラメータとして渡します。次に、結果を新しい
Map
** にまとめる必要があります。そのためには
Collectors.toMap()
を使用できます。
Map<String, Employee> result = combined.collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
その結果、コレクターは既存のマップのキーと値を使用します。しかし、この解決策は完璧というわけではありません。私たちのコレクターが重複したキーを持つエントリを満たすとすぐに、それは
IllegalStateException
をスローします。
この問題を処理するために、コレクターに3番目の「合併」ラムダパラメーターを追加するだけです。
(value1, value2) -> new Employee(value2.getId(), value1.getName())
重複キーが検出されるたびにラムダ式を使用します。
最後に、まとめてみましょう。
Map<String, Employee> result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(value1, value2) -> new Employee(value2.getId(), value1.getName())));
最後に、コードを実行して結果を確認しましょう。
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=3, name='Henry'}
見てのとおり、キー
“ Henry”
を持つ重複エントリは新しいキーと値のペアにマージされました。
新しい
Employee
のIDは
map2
から選択され、値は
map1
から選択されました。
5. Stream.of()
Stream
APIを引き続き使用するには、
Stream.of()
を使用して
Map
インスタンスを統合ストリームに変換できます。
ここでは、ストリームを扱うために追加のコレクションを作成する必要はありません。
Map<String, Employee> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new Employee(v1.getId(), v2.getName())));
まず、
map1
と
map2
を単一のストリームに変換します
。次に、ストリームをマップに変換します。ご覧のとおり、
toMap()
の最後の引数はマージ関数です。
v1
エントリからidフィールドを選び、
v2
から名前を選ぶことで、重複キーの問題を解決します。
プログラム実行後の印刷された
map3
インスタンス
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=1, name='Henry'}
6. 単純ストリーミング
さらに、
__stream()
pipelineを使ってマップエントリを組み立てることができます。以下のコードスニペットは、重複したエントリを無視して
map2
と
map1__のエントリを追加する方法を示しています。
Map<String, Employee> map3 = map2.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new Employee(v1.getId(), v2.getName()),
() -> new HashMap<>(map1)));
予想通り、マージ後の結果は次のとおりです。
{John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
George=Employee{id=2, name='George'},
Henry=Employee{id=1, name='Henry'}}
7.
StreamEx
JDKが提供するソリューションに加えて、人気のある
__StreamEx
__libraryも使用できます。
簡単に言うと、
StreamEx
は、
Stream
API
の拡張であり、その他にも多くの便利なメソッドが用意されています。キーと値のペアを操作するために
EntryStream
インスタンスを使用します
。
Map<String, Employee> map3 = EntryStream.of(map1)
.append(EntryStream.of(map2))
.toMap((e1, e2) -> e1);
アイデアは私たちの地図の流れを一つにまとめることです。それからエントリを新しい
map3
インスタンスに集めます。言及することが重要です、それは重複キーを扱うための規則を定義するのを助けるので
(e1、e2) – > e1
式。それがないと、コードは
IllegalStateException
をスローします。
そして今、結果:
{George=Employee{id=2, name='George'},
John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
Henry=Employee{id=1, name='Henry'}}
8概要
この短い記事では、Java 8でマップをマージするさまざまな方法を学びました。具体的には、
Map.merge()、Stream API、StreamEx
libraryを使用しました。
いつものように、議論の間に使用されたコードはhttps://github.com/eugenp/tutorials/tree/master/java-collections-maps[GitHubについて]で見つけることができます。