1. Введение

В этом кратком руководстве мы продемонстрируем три различных подхода к извлечению ключа из карты для заданного значения. Мы также обсудим положительные и отрицательные стороны различных решений.

Чтобы узнать больше об интерфейсе карты, вы можете прочитать эту статью.

2. Итеративный подход

Интерфейс Map в Java Collections предлагает метод entrySet(). Он возвращает все записи или пары ключ-значение карты в наборе.

Идея состоит в том, чтобы выполнить итерацию по этому набору записей и вернуть ключ, для которого значение соответствует предоставленному значению:

Однако может быть вероятность того, что несколько ключей указывают на одно и то же значение.

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, содержащий все нужные ключи:

Хотя это очень прямолинейная реализация, она сравнивает все записи, даже если все совпадения найдены после нескольких итераций.

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 мы можем сделать это более гибким и удобочитаемым способом. Мы преобразуем набор записей в поток и предоставляем лямбду для фильтрации только тех записей с заданным значением.

Затем мы используем метод map для возврата потока ключей из отфильтрованных записей:

Преимущество возврата потока заключается в том, что он может удовлетворить широкий спектр потребностей клиентов. Вызывающий код может потребовать только один ключ или все ключи, указывающие на предоставленное значение. Поскольку оценка потока выполняется лениво, клиент может контролировать количество итераций в зависимости от своих требований.

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);
}

Кроме того, клиент может преобразовать поток в любую коллекцию с помощью соответствующего сборщика:

4. Использование коллекций Apache Commons

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());

Приведенные выше идеи не будут очень полезны, если нам нужно вызывать функции очень часто для конкретной карты. Он будет без необходимости повторять набор своих ключей снова и снова.

В этом сценарии сохранение другого сопоставления значения с ключами имеет больше смысла, так как для извлечения ключа для значения потребуется постоянное время.

Библиотека Commons Collections от Apache предоставляет такую ​​двунаправленную карту под названием BidiMap. У него есть метод с именем getKey() для получения ключа для заданного значения:

Однако BidiMap устанавливает отношение 1:1 между своими ключами и значениями. Если мы попытаемся поместить пару ключ-значение, для которой значение уже существует на карте, старая запись будет удалена. Другими словами, он обновляет ключ по значению.

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

Также требуется больший объем памяти для хранения обратной карты.

Более подробная информация о том, как использовать BidiMap, содержится в этом руководстве.

5. Использование Google Guava

Мы можем использовать другую двунаправленную карту под названием BiMap, найденную в Guava, разработанную Google. Этот класс предоставляет метод с именем inverse() для получения Карты ключа значения или обратной Карты для выборки ключа на основе заданного значения: такое же значение. Если мы попытаемся сделать такую ​​попытку, она выдаст исключение java.lang.IllegalArgumentException.

Излишне говорить, что BiMap также использует значительный объем памяти, так как он должен хранить обратную карту внутри. Если вам интересно узнать больше о BiMap, вы можете ознакомиться с этим руководством.

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

6. Заключение

В этой краткой статье мы обсудили некоторые методы получения ключа Карты по заданному значению. Каждый подход имеет свои плюсы и минусы. Мы всегда должны рассматривать варианты использования и выбирать наиболее подходящий в зависимости от ситуации.

Полный исходный код приведенного выше руководства доступен на GitHub.

«

The complete source code for the above tutorial is available over on GitHub.