Как перевернуть Map, чтобы ключ стал значением, а значение ключом?

Рейтинг: -1Ответов: 4Опубликовано: 09.04.2023
Student student1 = new Student("Ivan", "Ivanov");
Student student2 = new Student("Nikolay", "Petrov");
Student student3 = new Student("Kirill", "Antipov");
Course matematic = new Course("Matematic");
Course philosophy = new Course("Philosophy");
Course english = new Course("English");
Course physics = new Course("Physics");
Course franch = new Course("Franch");

Map<Student, List<Course>> education = new HashMap<Student, List<Course>>();

education.put(student1, Arrays.asList(matematic, english, philosophy));
education.put(student2, Arrays.asList(matematic, english, physics));
education.put(student3, Arrays.asList());

Совсем не понимаю, как это возможно осуществить, не создавая новую карту.

Ответы

▲ 0Принят

Как по мне, то это изобретение велосипеда. Эту проблему давно решили. Например, таким решением есть bimap от guava. Метод, который перевернет Map, называется inverse.

Подробный гид можете найти здесь:

https://www.baeldung.com/guava-bimap

▲ 4

Если нужно построить "обратную" мапу вида Map<Course, List<Student>>, можно использовать метод Map::computeIfAbsent, чтобы создать список значений для "нового" ключа, и добавить в него "старый" ключ.

Обобщённое решение для любых пар типов K, V (для итерации используются методы Map::forEach, List::forEach)

public static<K, V> Map<V, List<K>> convert(Map<K, List<V>> map) {
    Map<V, List<K>> result = new HashMap<>(); // new LinkedHashMap<>();
    map.forEach((key, vals) -> vals.forEach(
        v -> result.computeIfAbsent(v, kk -> new ArrayList<>()).add(key)
    ));
    return result;
}

Возможно решение при помощи Stream API, но оно будет более громоздкое

public static<K, V> Map<V, List<K>> convert(Map<K, List<V>> map) {
    return map.entrySet()
        .stream() // Stream<Map.Entry<K, List<V>>>
        .flatMap(e -> e.getValue().stream() // Stream<V>
            .map(v -> Map.entry(v, e.getKey())) 
        )         // Stream<Map.Entry<V, K>>
        .collect(Collectors.groupingBy(
            Map.Entry::getKey,
            Collectors.mapping(Map.Entry::getValue, Collectors.toList())
        ));
}
▲ 0

Как-то так (писал тут, так что могут быть ошибки):

var res = new HashMap<Course, List<Student>>();

for (var entry : education.entrySet()) {
  var student = entry.getKey();

  for (var course : entry.getValue()) {
    var list = res.get(course);
    if (list == null) res.put(course, list = new ArrayList());
    list.add(student);
  }
}
▲ 0
    HashMap<String, String> countryCapitalMap = new HashMap<>();
    for (Map.Entry<String,String> enty: capitalCountryMap.entrySet()) {
        countryCapitalMap.put(enty.getValue(), enty.getKey());
    }

я сделала так