Переписывание значений в HashMap, которые находятся в ArrayList <String>

Скажем, у меня есть HashMap со строковыми клавишами и значениями Integer:

map = {cat=1, kid=3, girl=3, adult=2, human=5, dog=2, boy=2}

Я хочу переключать ключи и значения, помещая эту информацию в другую HashMap. Я знаю, что HashMap не может иметь дубликаты ключей, поэтому я попытался помещать информацию в HashMap с помощью Integer для ключей, которые будут отображаться в String ArrayList, чтобы я мог потенциально иметь одно целочисленное сопоставление с несколькими строками:

swap = {1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}

Я попробовал следующий код:

HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();

for (String x : map.keySet()) {
        for (int i = 0; i <= 5; i++) {
            ArrayList<String> list = new ArrayList<String>();
            if (i == map.get(x)) {
                list.add(x);
                swap.put(i, list);
            }
        }
    }

Единственное различие в моем коде состоит в том, что я не усложнил код 5 в свой индекс; У меня есть метод, который находит самое высокое целочисленное значение в исходном HashMap и использовал это. Я знаю, что он работает правильно, потому что я получаю тот же результат, даже если я жестко кодирую 5, я просто не использовал его для экономии места.

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

swap = {1=[cat], 2=[boy], 3=[girl], 5=[human]}

Как вы можете видеть, моя проблема в том, что значение ArrayList удерживает только последнюю строну, которая была помещена в нее, а не собирать все из них. Как заставить ArrayList хранить каждую строку, а не только последнюю строку?

Ответы

Ответ 1

С помощью Java 8 вы можете сделать следующее:

Map<String, Integer> map = new HashMap<>();

map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);

Map<Integer, List<String>> newMap = map.keySet()
                                       .stream()
                                       .collect(Collectors.groupingBy(map::get));

System.out.println(newMap);

Выход будет:

{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}

Ответ 2

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

for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    List<String> get = swap.get(value);
    if (get == null) {
        get = new ArrayList<>();
        swap.put(value, get);
    }
    get.add(key);
}

Ответ 3

Лучший способ - перебрать набор ключей исходной карты.

Также вы должны убедиться, что List присутствует для любого ключа целевой карты:

for (Map.Entry<String,Integer>  inputEntry : map.entrySet())
  swap.computeIfAbsent(inputEntry.getValue(),()->new ArrayList<>()).add(inputEntry.getKey());

Ответ 4

Это, очевидно, не лучшее решение, но подходит к проблеме так же, как вы сделали, заменив внутренние и внешние контуры, как показано ниже.

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("cat", 1);
    map.put("kid", 3);
    map.put("girl", 3);
    map.put("adult", 2);
    map.put("human", 5);
    map.put("dog", 2);
    map.put("boy", 2);

    HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();

    for (Integer value = 0; value <= 5; value++) {
        ArrayList<String> list = new ArrayList<String>();
        for (String key : map.keySet()) {
            if (map.get(key) == value) {
                list.add(key);
            }
        }
        if (map.containsValue(value)) {
            swap.put(value, list);
        }
    }

Выход

{1 = [cat], 2 = [взрослый, собака, мальчик], 3 = [ребенок, девочка], 5 = [человек]}

Ответ 5

Лучший способ, которым я могу думать, - использовать Map.forEach метод на существующей карте и Map.computeIfAbsent на новой карте:

Map<Integer, List<String>> swap = new HashMap<>();
map.forEach((k, v) -> swap.computeIfAbsent(v, k -> new ArrayList<>()).add(k));

В качестве дополнительной заметки вы можете использовать оператор алмаза <> для создания новой карты (нет необходимости повторять тип ключа и значение при вызове конструктора карты, поскольку компилятор сделает их вывод).

В качестве второй заметки хорошо использовать интерфейсные типы вместо конкретных типов, как для типичных типов параметров, так и для реальных типов. Вот почему я использовал List и Map вместо ArrayList и HashMap соответственно.

Ответ 6

Используя groupingBy, как в ответ Джейкоба, но с Map.entrySet для лучшей производительности, как предложенной Борисом:

// import static java.util.stream.Collectors.*

Map<Integer, List<String>> swap = map.entrySet()
    .stream()
    .collect(groupingBy(Entry::getValue, mapping(Entry::getKey, toList())));

Это использует еще два метода Collectors: mapping и toList.

Если бы не эти две вспомогательные функции, решение могло бы выглядеть так:

Map<Integer, List<String>> swap = map.entrySet()
    .stream()
    .collect(
        groupingBy(
            Entry::getValue,
            Collector.of(
                ArrayList::new,
                (list, e) -> {
                    list.add(e.getKey());
                },
                (left, right) -> { // only needed for parallel streams
                    left.addAll(right);
                    return left;
                }
            )
        )
    );

Или, используя toMap вместо groupingBy:

Map<Integer, List<String>> swap = map.entrySet()
    .stream()
    .collect(
        toMap(
            Entry::getValue,
            (e) -> new ArrayList<>(Arrays.asList(e.getKey())),
            (left, right) -> {
                left.addAll(right);
                return left;
            }
        )
    );

Ответ 7

Это означает, что вы переопределяете значения instrad, добавляя их к уже созданному arraylist. Попробуйте следующее:

HashMap<Integer, ArrayList<String>> swapedMap = new HashMap<Integer, ArrayList<String>>();
for (String key : map.keySet()) {
    Integer swappedKey = map.get(key);

    ArrayList<String> a = swapedMap.get(swappedKey);
    if (a == null) {
        a = new ArrayList<String>();
        swapedMap.put(swappedKey, a)
    }

    a.add(key);
}

У меня не было времени запустить его (извините, у меня нет компилятора Java), но должно быть почти нормально:)

Ответ 8

Вы можете использовать новый метод merge в java-8 из Map:

   Map<Integer, List<String>> newMap = new HashMap<>();

    map.forEach((key, value) -> {
        List<String> values = new ArrayList<>();
        values.add(key);
        newMap.merge(value, values, (left, right) -> {
            left.addAll(right);
            return left;
        });
    });