Мультикарта (multi-map) может пригодиться в реальной работе или на собеседовании. Почему-то в некоторых компаниях при приёме на работу любят давать алгоритмические задачки на её использование (лично я так не поступаю).
По сути это обычная карта (Map) в которой значением является коллекция (List или Set).
Сейчас в JCF (Java Collections Framework) нет готового класса для Multimap.
До выхода Java 8 приходилось логику работы писать вручную.
Например так.
Допустим, нам поступают такие данные:
Moscow=ru
Omsk=ru
Tula=ru
NY=us
LA=us
London=uk и т.д.
Их нужно представить в виде:
ru=Moscow, Omsk, Tula
us=NY, LA
uk=London
Тогда логику можно реализовать, например, в таком виде:
import java.util.*; public class Main { public static void main(String[] args) { // Получили город и страну String city = "Moscow", country = "ru"; // Карта куда всё будем складывать Map<String, List<String>> map = new HashMap<>(); List<String> list = map.get(country); // Если это наш первый раз, // тогда создаем список и кладем его по ключу (название страны) if (list == null) { list = new ArrayList<>(); map.put(country, list); } list.add(city); } } |
Это не удобно, поэтому многие используют Multimap из Apache Commons или Google Guava.
В восьмой Java, с помощью анонимных функций и нового API, логику можно сделать однострочником:
// В случае первого раза вызывать функцию: (key) -> value map.computeIfAbsent(country, (k)-> new ArrayList<String>()).add(city); |
N.B.: Совет использовать computeIfAbsent для создания мультикарты явно указан в документации.
Если же у нас на входе готовая карта, то её можно прокрутить foreach (хотя это не очень красиво):
Например так:
Map<String, String> map = new HashMap<>(); map.put("Madrid", "es"); map.put("Moscow", "ru"); map.put("Omsk", "ru"); map.put("London", "uk"); Map<String,List<String>> result = new HashMap<>(); map.forEach((city, country) -> { result.computeIfAbsent(country, (k) -> new ArrayList<String>()).add(city); }); |
Интересней было бы воспользоваться Stream API.
Например так:
import java.util.*; import static java.util.Map.Entry; import static java.util.stream.Collectors.*; public class Main { public static void main(String[] args) { Map<String, String> map = new HashMap<>(); map.put("Madrid", "es"); map.put("Moscow", "ru"); map.put("Omsk", "ru"); map.put("London", "uk"); // Сгруппировать по стране (Entry::getValue), // а название города Entry::getKey записывать в список (toList) Map<String, List<String>> m = map.entrySet().stream().collect( // Группировать по названию страны (es, ru, uk и т.д.) groupingBy(Entry<String, String>::getValue, // Запихиваем город (Moscow, London и т.д.) в список (toList) mapping(Entry<String, String>::getKey, toList()))); } } |
Здесь используется функциональное программирование, восприятие которого требует определенного "привыкания". Поскольку я периодически сталкиваюсь как со сторонниками "императивщины", так и со сторонниками "функциональщины", могу сказать, что у каждого своя правда. В каждом конкретном случае следует исходить из конечных целей.
В качестве причины почему действительно стоит изучать функциональный поход могу сказать, что игнорировать его сейчас уже нельзя, т.к. в противном случае программист просто перестанет понимать программы написанные другими программистами. Когда появилась 5-ая Java было очень много ненавистников дженериков, сейчас польза их очевидна, а умение их использовать просто необходимо.