Как обновить значение, учитывая ключ в hashmap? - PullRequest
558 голосов
/ 11 ноября 2010

Предположим, у нас есть HashMap<String, Integer> в Java.

Как обновить (увеличить) целочисленное значение ключа строки для каждого существования найденной строки?

Можно было бы удалить и повторно ввести пару, но это было бы проблемой.
Другим способом было бы просто поставить новую пару и заменить старую.

В последнем случае, что произойдет, еслиесть конфликт хеш-кода с новым ключом, который я пытаюсь вставить?Правильное поведение для хеш-таблицы будет состоять в том, чтобы назначить для нее другое место или составить список из нее в текущем сегменте.

Ответы [ 17 ]

874 голосов
/ 11 ноября 2010
map.put(key, map.get(key) + 1);

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

93 голосов
/ 11 сентября 2014

Java 8 way:

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

Например,

Map<String, Integer> words = new HashMap<>();
words.put("hello", 3);
words.put("world", 4);
words.computeIfPresent("hello", (k, v) -> v + 1);
System.out.println(words.get("hello"));

В качестве альтернативы можно использовать метод merge, где 1 - это значение по умолчанию, а функция увеличивает существующее значение на 1:

words.merge("hello", 1, Integer::sum);

Кроме того, существует множество других полезных методов, таких как putIfAbsent, getOrDefault, forEach и т. Д.

49 голосов
/ 03 ноября 2011
hashmap.put(key, hashmap.get(key) + 1);

Метод put заменит значение существующего ключа и создаст его, если он не существует.

37 голосов
/ 08 июня 2016

Упрощенный Java 8 путь:

map.put(key, map.getOrDefault(key, 0) + 1);

При этом используется метод HashMap, который получает значение для ключа, но если ключ не может быть получен, он возвращаетуказанное значение по умолчанию (в данном случае '0').

Это поддерживается в ядре Java: getOrDefault(Object key, V defaultValue)"> HashMapgetOrDefault (ключ объекта, V defaultValue)

27 голосов
/ 11 ноября 2010

Замените Integer на AtomicInteger и вызовите один из методов incrementAndGet / getAndIncrement.

Альтернативой является добавление int в ваш собственный класс MutableInteger, который имеет метод increment(), вам нужно решить только проблему безопасности потоков.

17 голосов
/ 11 ноября 2010

@ Решение Мэтью является самым простым и в большинстве случаев будет работать достаточно хорошо.

Если вам нужна высокая производительность, AtomicInteger - лучшее решение, аля @ BalusC.

Однако, более быстрое решение(при условии, что безопасность потоков не является проблемой) должен использовать TObjectIntHashMap , который предоставляет метод приращения (ключ) и использует примитивы и меньше объектов, чем при создании AtomicIntegers.например,

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
map.increment("aaa");
16 голосов
/ 06 декабря 2016

Однолинейное решение:

map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);
12 голосов
/ 09 августа 2013

Вы можете увеличивать, как показано ниже, но вам нужно проверить существование, чтобы исключение NullPointerException не выдавалось

if(!map.containsKey(key)) {
 p.put(key,1);
}
else {
 p.put(key, map.getKey()+1);
}
9 голосов
/ 04 марта 2014

Существует ли хэш (со значением 0) или он «помещен» на карту с первым шагом? Если он «помещен» в первый инкремент, код должен выглядеть следующим образом:

if (hashmap.containsKey(key)) {
    hashmap.put(key, hashmap.get(key)+1);
} else { 
    hashmap.put(key,1);
}
6 голосов
/ 24 мая 2016

Может быть немного поздно, но вот мои два цента.

Если вы используете Java 8, то вы можете использовать метод computeIfPresent .Если значение для указанного ключа присутствует и не равно нулю, то оно пытается вычислить новое сопоставление, учитывая ключ и его текущее сопоставленное значение.

final Map<String,Integer> map1 = new HashMap<>();
map1.put("A",0);
map1.put("B",0);
map1.computeIfPresent("B",(k,v)->v+1);  //[A=0, B=1]

Мы также можем использовать другой метод putIfAbsent чтобы поставить ключ.Если указанный ключ еще не связан со значением (или сопоставлен со значением NULL), этот метод связывает его с данным значением и возвращает значение NULL, в противном случае возвращается текущее значение.

В случае, если карта используется совместнотогда мы можем использовать ConcurrentHashMap и AtomicInteger .Из документа:

AtomicInteger - это значение типа int, которое может быть обновлено атомарно.AtomicInteger используется в приложениях, таких как счетчики с атомным приращением, и не может использоваться в качестве замены для Integer.Однако этот класс расширяет Number, чтобы обеспечить единообразный доступ инструментам и утилитам, которые работают с классами на основе чисел.

Мы можем использовать их, как показано ниже:

final Map<String,AtomicInteger> map2 = new ConcurrentHashMap<>();
map2.putIfAbsent("A",new AtomicInteger(0));
map2.putIfAbsent("B",new AtomicInteger(0)); //[A=0, B=0]
map2.get("B").incrementAndGet();    //[A=0, B=1]

Одна точкачтобы наблюдать, мы вызываем get, чтобы получить значение для ключа B, а затем вызываем incrementAndGet() для его значения, которое, конечно, AtomicInteger.Мы можем оптимизировать его, так как метод putIfAbsent возвращает значение для ключа, если он уже существует:

map2.putIfAbsent("B",new AtomicInteger(0)).incrementAndGet();//[A=0, B=2]

На заметку, если мы планируем использовать AtomicLong , то в соответствии с документациейожидаемая пропускная способность при высокой конкуренции LongAdder значительно выше за счет более высокого потребления пространства.Также проверьте этот вопрос .

...