Подтвердить что ты не робот

Java: как получить набор ключей с одинаковым значением в hashmap

У меня есть hashmap, как показано ниже:

1- > х

2- > у

3- > х

4- > г

Теперь я хочу знать все ключи, значение которых равно x (ans: [1,3]). что лучший способ сделать?

Метод грубой силы - просто перебрать по карте и сохранить все ключи в массиве, значение которого равно x.

Есть ли эффективный способ для этого.

Спасибо

4b9b3361

Ответ 1

Вы можете использовать MultiMap, чтобы легко получить все эти повторяющиеся значения.

Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "x");
map.put(2, "y");
map.put(2, "z");
map.put(3, "x");
map.put(4, "y");
map.put(5, "z");
map.put(6, "x");
map.put(7, "y");

System.out.println("Original map: " + map);

Multimap<String, Integer> multiMap = HashMultimap.create();
for (Entry<Integer, String> entry : map.entrySet()) {
  multiMap.put(entry.getValue(), entry.getKey());
}
System.out.println();

for (Entry<String, Collection<Integer>> entry : multiMap.asMap().entrySet()) {
  System.out.println("Original value: " + entry.getKey() + " was mapped to keys: "
      + entry.getValue());
}

Распечатывает:

Original map: {1=x, 2=z, 3=x, 4=y, 5=z, 6=x, 7=y}

Original value: z was mapped to keys: [2, 5]
Original value: y was mapped to keys: [4, 7]
Original value: x was mapped to keys: [1, 3, 6]

Per @ noahz, forMap и invertFrom занимает меньше строк, но, возможно, более сложно читать:

HashMultimap<String, Integer> multiMap =
    Multimaps.invertFrom(Multimaps.forMap(map), 
        HashMultimap.<String, Integer> create());

вместо:

Multimap<String, Integer> multiMap = HashMultimap.create();
for (Entry<Integer, String> entry : map.entrySet()) {
  multiMap.put(entry.getValue(), entry.getKey());
}

Ответ 2

Хешмап - это структура, оптимизированная для ассоциативного доступа значений с помощью ключей, но никоим образом не лучше делать обратное, чем массив. Я не думаю, что вы можете сделать лучше, чем просто повторить. Единственный способ повысить эффективность - это иметь обратную хэш-карту (например, хэш-карту, где вы держите массив ключей, указывающих на заданное значение для всех значений).

Ответ 3

Если Java 8 является опцией, вы можете попробовать использовать потоковый подход:

Map<Integer, String> map = new HashMap<>();
map.put(1, "x");
map.put(2, "y");
map.put(3, "x");
map.put(4, "z");

Map<String, ArrayList<Integer>> reverseMap = new HashMap<>(
    map.entrySet().stream()
        .collect(Collectors.groupingBy(Map.Entry::getValue)).values().stream()
        .collect(Collectors.toMap(
                item -> item.get(0).getValue(),
                item -> new ArrayList<>(
                    item.stream()
                        .map(Map.Entry::getKey)
                        .collect(Collectors.toList())
                ))
        ));

System.out.println(reverseMap);

Результат:

{x=[1, 3], y=[2], z=[4]}

Если Java 7 является предпочтительным:

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

for (Map.Entry<Integer,String> entry : map.entrySet()) {
    if (!reverseMap.containsKey(entry.getValue())) {
        reverseMap.put(entry.getValue(), new ArrayList<>());
    }
    ArrayList<Integer> keys = reverseMap.get(entry.getValue());
    keys.add(entry.getKey());
    reverseMap.put(entry.getValue(), keys);
}

Как интересно, я экспериментировал со временем, требуемым для каждого алгоритма при выполнении больших карт (индексных, случайных ('a' - 'z') пар.

              10,000,000        20,000,000
Java 7:         615 ms            11624 ms         
Java 8:        1579 ms             2176 ms

Ответ 4

Если вы открыты для использования библиотеки, используйте утилиты Google Guava Multimaps, в частности forMap() в сочетании с invertFrom()

Ответ 5

Да, просто грубая сила. Вы можете сделать это быстро, также сохраняя Multimap из Value → Collection of Key, за счет памяти и времени исполнения для обновлений.

Ответ 6

HashMap вычисляет hashcode() ключа, а не значений. Если вы не храните какую-либо дополнительную информацию или не используете другую структуру данных, я думаю, что единственный способ получить это - грубая сила.

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

Ответ 7

Если вы используете хэш-карту, нет эффективного способа сделать это, но итерации значений

Ответ 8

Если у вас уже есть карта, вам следует использовать Google Guava для фильтрации записей, которые вам интересны. Вы можете что-то сделать вдоль линий:

final Map<Integer, Character> filtered = Maps.filterValues(unfiltered, new Predicate<Character>() {
    @Override
    public boolean apply(Character ch) {
        return ch == 'x';
    }
});

Ответ 9

Я согласен с Джорджем Кэмпбеллом, но для java 8 я бы сделал это немного легче:

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

Ответ 10

Попробуйте это.....

public static void main(String[] args) {
        HashMap<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("cust_tenure", "3_sigma");
        hashMap.put("cust_age", "3_sigma");
        hashMap.put("cust_amb_6m_sav", "3_sigma");
        hashMap.put("cust_amb_6m_chq", "3_sigma");
        hashMap.put("cust_total_prod_6m", "3_sigma");

        HashMap<String, ArrayList<String>> result = new LinkedHashMap<String, ArrayList<String>>();

        for (String key : hashMap.keySet()) {
            ArrayList<String> colName = null;
            if (!result.containsKey(hashMap.get(key))) {
                colName = new ArrayList<String>();
                colName.add(key);
                result.put(hashMap.get(key), colName);
            } else {
                colName = result.get(hashMap.get(key));
                colName.add(key);
                result.put(hashMap.get(key), colName);
            }

            System.out.println(key + "\t" + hashMap.get(key));
        }

        for (String key : result.keySet()) {
            System.out.println(key + "\t" + result.get(key));
        }

        System.out.println(hashMap.size());

    }