Этот урок Java научит нас, как инвертировать заданную Map с помощью различных методов. Мы научимся инвертировать Maps с уникальными значениями и создавать Multimap при наличии дублирующихся значений.
1. Что такое перевернутая карта?
Инвертированная карта <V, K> является экземпляром исходной карты<K, V>. Значения исходной карты становятся ключами в результирующей карте, а ключи становятся значениями.
Карта карта = Карта.of("ключ1", "значение1", "ключ2", "значение2");System.out.println(map); //{ключ1=значение1, ключ2=значение2}//Инвертированные записи карты{значение1=ключ1, значение2=ключ2}
2. Инвертирование карты с уникальными значениями
Для инвертирования карты, содержащей уникальные значения, следует использовать следующие подходы, поскольку мы не применяем никаких механизмов для разрешения повторяющихся ключей/значений.
2.1 Использование цикла for
Самый простой способ инвертировать карту — использовать цикл. Мы перебираем записи карты и добавляем их в новую карту. При добавлении записей мы меняем ключи и значения друг с другом.
Map<Integer, String> invertedMap = new HashMap<>();for(Map.Entry<String, Integer> entry : originalMap.entrySet()) {invertedMap.put(entry.getValue(), entry.getKey());}
2.2 Использование Collectors.toMap()
Stream API предоставляет Collectors.toMap() для удобного сбора элементов Stream в Map. Нам нужно перебрать элементы Stream и собрать записи в обратном порядке.
Map<Integer, String> inverseMap = originalMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
3. Инвертирование карты с повторяющимися значениями
Предположим, что в Map есть несколько записей с дублирующимися значениями. Теперь при инвертировании Map мы должны решить, как обращаться с этими значениями, поскольку инвертированная Map не допустит дублирования ключей.
Как правило, создание MultiMap является правильным решением для таких случаев. MultiMap похож на Map с тем дополнением, что несколько элементов могут иметь одинаковые ключи.
3.1 Использование цикла for
В следующем примере список используется для хранения нескольких значений ключа, что приводит к созданию мультиотображения.
Map<Integer, String> mapWithDuplicateValues = new HashMap<Integer, String>();mapWithDuplicateValues.put(1, "Value1");mapWithDuplicateValues.put(2, "Value2");mapWithDuplicateValues.put(3, "Value2");HashMap<String, List<Integer>> inverseMap = new HashMap<String, List<Integer>>();for(Map.Entry<Integer, String> entry : mapWithDuplicateValues.entrySet()) {if(inverseMap.containsKey(entry.getValue())) {inverseMap.get(entry.getValue()).add(entry.getKey());} else {List<Integer> list = new ArrayList<Integer>();list.add(entry.getKey());inverseMap.put(entry.getValue(), list);}}System.out.println(inverseMap); //{Value1=[1], Value2=[2, 3]}
3.2 Использование Collectors.groupBy()
В следующем примере функция Collectors.mapping() выполняет операцию сокращения повторяющихся значений, передавая их сборщику, который используется для сбора повторяющихся значений в список.
HashMap<String, List<Integer>> inverseMap = originalMap.entrySet().stream().collect(Collectors.groupingBy(Entry::getValue,Collectors.mapping(Entry::getKey, Collectors.toList())));
4. Использование библиотеки Guava
Guava — это библиотека Java с открытым исходным кодом, созданная Google. Она предоставляет множество полезных коллекций и интерфейсов. Мы будем использовать следующие карты для инвертирования заданной карты.
4.1 Использование Multimap
Multimap представляет собой простую реализацию, которая управляет дублирующимися значениями изнутри.
Multimap<Integer, String> multimap = ImmutableMultimap.of(1, "Key1", 1, "Key2", 2, "Key3");System.out.println(multimap); //{1=[Key1, Key2], 2=[Key3]}
4.2 Использование BiMap
BiMap — это двунаправленная карта, которая поддерживает инверсный вид карты, т.е. записи с обратными ключами и значениями. Эта карта также поддерживает, что в карте не хранятся дублирующиеся значения или ключи.
BiMap<Integer, String> biMap = ImmutableBiMap.of(1, "Key1", 2, "Key2", 3, "Key3");BiMap<String, Integer> inverseBiMap = biMap.inverse();System.out.println(inverseBiMap); //{Key1=1, Key2=2, Key3=3}
Есть еще один класс HashBiMap, поддерживаемый двумя хэш-таблицами, одна для сопоставления ключа со значением, а другая для сопоставления значения с ключом. Эта реализация допускает нулевые ключи и значения.
BiMap<Integer,String> originalMap = HashBiMap.create();//add key-value pairsBiMap<String, Integer> inversedMap = biMap.inverse();
5. Использование Apache Commons-Collections
Библиотека Apache commons collections предлагает различные реализации и утилиты для облегчения обработки коллекций. Мы будем использовать методы BidiMap и inverseBidiMap() для инвертирования карты, очень похоже на то, что мы делали для Guava.
5.1 Использование BidiMap
Интерфейс BidiMap позволяет осуществлять двунаправленный поиск между ключами и значениями. Вот различные реализации этого интерфейса:
- DualHashBidiMap: использует два экземпляра HashMap, которые обеспечивают быстрый поиск записей с использованием ключа или значения.
- DualLinkedHashBidiMap: использует два экземпляра LinkedHashMap, которые обеспечивают быстрый поиск за счет хранения двух наборов связанных списков и записей карты.
- TreeBidiMap: Эта карта основана на реализации красно-черного дерева. Она гарантирует, что ключи-значения будут храниться в порядке возрастания в соответствии с естественным порядком ключей и значений.
- DualTreeBidiMap: использует два экземпляра TreeMap, что делает его более затратным, чем TreeBidiMap, поскольку он хранит каждый объект дважды.
Мы можем использовать метод inverseBidiMap(), чтобы получить инвертированный вид исходной карты. Обратите внимание, что при обнаружении дублирующихся значений более поздний ключ имеет приоритет над предыдущим.
BidiMap bidiMap = new DualHashBidiMap();bidiMap.put(1, "Value1");bidiMap.put(2, "Value2");System.out.println(bidiMap.inverseBidiMap()); //{Value1=1, Value2=2}
5.2. Использование MapUtils.invertMap()
Метод invertMap() возвращает новый HashMap, в котором ключи и значения меняются местами. Обратите внимание, что если исходная карта имеет дублирующиеся значения для нескольких ключей, возвращаемая карта будет содержать один из этих ключей, но точный ключ, который должен быть сопоставлен, будет неопределен.
Map<Integer, String> hashMap = new HashMap<Integer, String>();hashMap.put(1,"Value1");hashMap.put(2,"Value2");hashMap.put(3,"Value2");Map<String,Integer> inversedMap1 = MapUtils.invertMap(hashMap);System.out.println(inversedMap1); //{Value1=1, Value2=3}
6. Заключение
В этом уроке Java мы научились инвертировать указанную карту, используя различные методы от цикла for до методов Guava и Apache-collections. Мы также инвертировали карты с дублирующимися значениями. Эффективный способ инвертировать заданную карту — использовать внешние библиотеки, такие как Guava или Apache-commons, вместо итерации по записям.