1. 概要

この簡単な記事では、Javaでマップを反転する方法を見ていきます。 アイデアは、の新しいインスタンスを作成することです地図タイプの特定のマップに対して地図 。 さらに、ソースマップに重複する値が存在する場合の処理方法についても説明します。

HashMapクラス自体の詳細については、他の記事を参照してください。

2. 問題の定義

いくつかのKey-Valueペアを持つMapがあるとしましょう。

Map<String, Integer> map = new HashMap<>();
map.put("first", 1);
map.put("second", 2);

元のマップには、次のようなアイテムが格納されます。

{first=1, second=2}

代わりに、キーを値に変換し、その逆を新しいMapオブジェクトに変換します。 結果は次のようになります。

{1=first, 2=second}

3. 従来のforループの使用

まず、forループを使用してマップを反転する方法を見てみましょう。

public static <V, K> Map<V, K> invertMapUsingForLoop(Map<K, V> map) {
    Map<V, K> inversedMap = new HashMap<V, K>();
    for (Entry<K, V> entry : map.entrySet()) {
        inversedMap.put(entry.getValue(), entry.getKey());
    }
    return inversedMap;
}

ここでは、 MapオブジェクトのentrySet()を繰り返し処理しています。 その後、オリジナルを追加します価値新品としてとオリジナル新しいものとして価値 reversedMap 物体言い換えると、 キーを値に置き換え、値をキーに置き換えて、マップの内容をコピーします 。 さらに、これは8より前のJavaバージョンに適していますが、このアプローチは、ソースマップの値が一意である場合にのみ機能することに注意してください

4. StreamAPIを使用してマップを反転する

Java 8は、 Stream APIから、より機能的なスタイルでMapを反転するための便利なメソッドを提供します。 それらのいくつかを見てみましょう。

4.1. Collectors.toMap()

ソースマップに重複する値がない場合は、 Collectors.toMap()を使用できます。

public static <V, K> Map<V, K> invertMapUsingStreams(Map<K, V> map) {
    Map<V, K> inversedMap = map.entrySet()
        .stream()
        .collect(Collectors.toMap(Entry::getValue, Entry::getKey));
    return inversedMap;
}

まず、 entrySet()がオブジェクトのストリームに変換されます。 続いて、 Collectors.toMap()を使用して、KeyValueinversedMapオブジェクトに収集しました。

ソースマップに重複する値が含まれていると考えてみましょう。 このような場合、マッピング関数を使用して、入力要素にカスタムルールを適用できます

public static <K, V> Map<V, K> invertMapUsingMapper(Map<K, V> sourceMap) {
    return sourceMap.entrySet()
        .stream().collect(
            Collectors.toMap(Entry::getValue, Entry::getKey, (oldValue, newValue) -> oldValue) 
        );
}

このメソッドでは、 Collectors.toMap()の最後の引数はマッピング関数です。 これを使用して、重複がある場合に追加するキーをカスタマイズできます。 上記の例では、ソースマップに重複する値が含まれている場合、最初の値をキーとして保持します。 ただし、値が繰り返される場合、保持できるキーは1つだけです。

4.2. Collectors.groupingBy()

ソースマップに重複する値が含まれている場合でも、すべてのキーが必要になる場合があります。 または、 Collectors.groupingBy()を使用すると、重複する値を処理するための制御が向上します

たとえば、次の Key Valueペアがあるとします。

{first=1, second=2, two=2}

ここでは、値「2」が異なるキーに対して2回繰り返されます。 このような場合、 groupingBy()メソッドを使用して、Valueオブジェクトにカスケードされた「groupby」操作を実装できます。

private static <V, K> Map<V, List<K>> invertMapUsingGroupingBy(Map<K, V> map) {
    Map<V, List<K>> inversedMap = map.entrySet()
        .stream()
        .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
    return inversedMap;
}

少し説明すると、 Collectors.mapping()関数は、指定されたコレクターを使用して、指定されたキーに関連付けられた値に対して削減操作を実行します。 groupingBy()コレクターは、重複する値を List に収集し、結果としてMultiMapになります。 出力は次のようになります。

{1=[first], 2=[two, second]}

5. 結論

この記事では、HashMapを反転するためのいくつかの組み込みの方法を例を挙げて簡単に確認しました。 また、Mapオブジェクトを反転するときに重複する値を処理する方法も確認しました。

一方、いくつかの外部ライブラリは、Mapインターフェイスに加えて追加機能を提供します。 以前、 Google GuavaBiMapApacheBidiMapを使用してMapを反転する方法を示しました。

いつものように、これらの例のコードはGitHubから入手できます。