Перейти к основному содержимому

Как инвертировать карту в Java

· 4 мин. чтения

1. Обзор

В этой быстрой статье мы рассмотрим, как инвертировать карту в Java . Идея состоит в том, чтобы создать новый экземпляр Map<V, K> для данной карты типа Map<K, V> . Кроме того, мы также увидим, как обрабатывать случай, когда в исходной карте присутствуют повторяющиеся значения.

Пожалуйста, обратитесь к другой нашей статье, чтобы узнать больше о самом классе HashMap .

2. Определение проблемы

Предположим, у нас есть карта с несколькими парами ключ-значение :

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

Исходная карта будет хранить такие элементы, как:

{first=1, second=2}

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

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

Здесь мы проходим через entrySet() объекта Map . После этого мы добавляем исходное значение в качестве нового ключа и исходный ключ в качестве нового значения в объект inversedMap . Другими словами, мы копируем содержимое карты, заменяя ключи на значения и значения на ключи . Кроме того, это подходит для версий Java до 8, хотя следует отметить, что этот подход работает только в том случае, если значения исходной карты уникальны .

4. Использование Stream API для инвертирования карты

Java 8 предоставляет удобные методы из Stream API для инвертирования Map в более функциональном стиле. Давайте посмотрим на некоторые из них.

4.1. Коллекторы.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() для сбора ключа и значения в объект inversedMap .

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

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() — это функция сопоставления. Используя это, мы можем настроить, какой ключ следует добавить в случае наличия дубликатов . В приведенном выше примере мы сохраняем первое значение в качестве ключа, если исходная карта содержит повторяющиеся значения. Однако мы можем сохранить только один ключ, если значения повторяются.

4.2. Коллекторы.groupingBy()

Иногда нам могут понадобиться все ключи, даже если исходная карта содержит повторяющиеся значения. В качестве альтернативы Collectors.groupingBy() обеспечивает лучший контроль над обработкой повторяющихся значений .

Например, предположим, что у нас есть следующая пара « ключзначение »:

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

Здесь значение «2» повторяется дважды для разных ключей. В этих случаях мы можем использовать метод groupingBy() для реализации каскадной операции «группировать по» для объектов Value :

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 .

Между тем, несколько внешних библиотек предоставляют дополнительные функции поверх интерфейса карты . Ранее мы демонстрировали, как инвертировать карту с помощью Google Guava BiMap и Apache BidiMap .

Как всегда, код этих примеров доступен на GitHub .