1. 序章

このクイックチュートリアルでは、特定の値のマップからキーを取得するための3つの異なるアプローチを示します。 また、さまざまなソリューションの長所と短所についても説明します。

Map インターフェースの詳細については、この記事をご覧ください。

2. 反復アプローチ

JavaコレクションMapインターフェースは、 entrySet()と呼ばれるメソッドを提供します。 Set内のマップのすべてのエントリまたはキーと値のペアを返します。

このエントリセットを繰り返し処理し、値が指定された値と一致するキーを返すという考え方です。

public <K, V> K getKey(Map<K, V> map, V value) {
    for (Entry<K, V> entry : map.entrySet()) {
        if (entry.getValue().equals(value)) {
            return entry.getKey();
        }
    }
    return null;
}

ただし、複数のキーが同じ値を指している可能性があります。

その場合、一致する値が見つかった場合は、 Set にキーを追加して、ループを続行します。 最後に、必要なすべてのキーを含むSetを返します。

public <K, V> Set<K> getKeys(Map<K, V> map, V value) {
    Set<K> keys = new HashSet<>();
    for (Entry<K, V> entry : map.entrySet()) {
        if (entry.getValue().equals(value)) {
            keys.add(entry.getKey());
        }
    }
    return keys;
}

これは非常に単純な実装ですが、数回の反復後にすべての一致が見つかった場合でも、すべてのエントリを比較します。

3. 機能的アプローチ

Java 8にLambda式が導入されたことで、より柔軟で読みやすい方法でそれを行うことができます。エントリセットを Stream に変換し、ラムダをフィルターのみに提供します指定された値を持つエントリ。

次に、mapメソッドを使用して、フィルタリングされたエントリからキーのStreamを返します。

public <K, V> Stream<K> keys(Map<K, V> map, V value) {
    return map
      .entrySet()
      .stream()
      .filter(entry -> value.equals(entry.getValue()))
      .map(Map.Entry::getKey);
}

ストリームを返すことの利点は、クライアントの幅広いニーズに対応できることです。呼び出し元のコードは、指定された値を指す1つのキーまたはすべてのキーのみを必要とする場合があります。 ストリームの評価は怠惰であるため、クライアントはその要件に基づいて反復回数を制御できます。

さらに、クライアントは適切なコレクターを使用してストリームを任意のコレクションに変換できます。

Stream<String> keyStream1 = keys(capitalCountryMap, "South Africa");
String capital = keyStream1.findFirst().get();

Stream<String> keyStream2 = keys(capitalCountryMap, "South Africa");
Set<String> capitals = keyStream2.collect(Collectors.toSet());

4. ApacheCommonsCollectionsの使用

上記のアイデアは、特定のマップに対して非常に頻繁に関数を呼び出す必要がある場合、あまり役に立ちません。 キーのセットを不必要に何度も繰り返します。

このシナリオでは、キーへの値の別のマップを維持する方が、値のキーを取得するのに一定の時間がかかるため、より理にかなっています。

ApacheによるCommonsCollections ライブラリは、BidiMapと呼ばれるそのような双方向のMapを提供します。 指定された値のキーを取得するためのgetKey()という名前のメソッドがあります。

BidiMap<String, String> capitalCountryMap = new DualHashBidiMap<>();
capitalCountryMap.put("Berlin", "Germany");
capitalCountryMap.put("Cape Town", "South Africa");
String capitalOfGermany = capitalCountryMap.getKey("Germany");

ただし、 BidiMapは、キーと値の間に1:1の関係を課します Map、に値がすでに存在するキーと値のペアを配置しようとすると、古いエントリが削除されます。 つまり、値に対してキーを更新します。

また、リバースマップを維持するために大量のメモリが必要になります。

BidiMap の使用方法の詳細は、このチュートリアルにあります。

5. GoogleGuavaを使用する

Googleが開発したGuavaで見つかったBiMapと呼ばれる別の双方向マップを使用する場合があります。このクラスは、値キー Mapを取得するためのinverse()という名前のメソッドを提供します]またはその逆Mapを使用して、指定された値に基づいてキーをフェッチします。

HashBiMap<String, String> capitalCountryMap = HashBiMap.create();
capitalCountryMap.put("Berlin", "Germany");
capitalCountryMap.put("Cape Town", "South Africa");
String capitalOfGermany = capitalCountryMap.inverse().get("Germany");

BidiMap と同様に、BiMapも同じ値を参照する複数のキーを許可しません。 作ろうとしたらそのような試み、それをスローします java.lang.IllegalArgumentException

言うまでもなく、 BiMap は、逆マップを内部に格納する必要があるため、大量のメモリも使用します。 BiMap について詳しく知りたい場合は、このチュートリアルをご覧ください。 

6. 結論

この簡単な記事では、値を指定してMapのキーを取得するいくつかの方法について説明しました。 それぞれのアプローチには、独自の長所と短所があります。 常にユースケースを検討し、状況に応じて最も適切なユースケースを選択する必要があります。

上記のチュートリアルの完全なソースコードは、GitHubから入手できます。