it-roy-ru.com

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

Допустим, у меня есть HashMap со строковыми ключами и целочисленными значениями:

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

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

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 хранить каждую строку, а не только последнюю строку?

15
jtrain17

С 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]}

22
Jacob G.

вы воссоздаете 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);
}
6
Zeromus

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

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

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

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

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

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

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

2
Federico Peralta Schaffner

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

    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 = [кошка], 2 = [взрослый, собака, мальчик], 3 = [малыш, девушка], 5 = [человек]}

2
Beginner

Использование 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;
            }
        )
    );
1
xehpuk

Вы можете использовать новый метод 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;
        });
    });
0
Eugene

Он предлагает вам переопределить значения instrad и добавить их в уже созданный массив. Попробуй это:

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), но все должно быть в порядке :)

0
Stefano Liboni