783

Suppose we have a HashMap<String, Integer> in Java.

How do I update (increment) the integer-value of the string-key for each existence of the string I find?

One could remove and reenter the pair, but overhead would be a concern.
Another way would be to just put the new pair and the old one would be replaced.

In the latter case, what happens if there is a hashcode collision with a new key I am trying to insert? The correct behavior for a hashtable would be to assign a different place for it, or make a list out of it in the current bucket.

TylerH
  • 20,799
  • 66
  • 75
  • 101
laertis
  • 8,229
  • 4
  • 21
  • 17

17 Answers17

1212
map.put(key, map.get(key) + 1);

should be fine. It will update the value for the existing mapping. Note that this uses auto-boxing. With the help of map.get(key) we get the value of corresponding key, then you can update with your requirement. Here I am updating to increment value by 1.

Steffen Harbich
  • 2,639
  • 2
  • 37
  • 71
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
145

Java 8 way:

You can use computeIfPresent method and supply it a mapping function, which will be called to compute a new value based on existing one.

For example,

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"));

Alternatevely, you could use merge method, where 1 is the default value and function increments existing value by 1:

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

In addition, there is a bunch of other useful methods, such as putIfAbsent, getOrDefault, forEach, etc.

Konstantin Milyutin
  • 11,946
  • 11
  • 59
  • 85
  • 4
    I just tested your solutions. The second one, the one with method reference, works. The first one, the lambda expression one, is not working consistently when any value of your map is `null` (say `words.put("hello", null);`), the result is still `null` not `1` as I would expect. – Tao Zhang May 15 '19 at 00:31
  • 5
    From Javadoc: "If the value for the specified key is present and non-null, attempts to compute a new mapping". You can use `compute()` instead, it will handle `null` values as well. – Konstantin Milyutin May 15 '19 at 15:40
  • 1
    I want to increment my value by 1. `.merge` is my solution with `Integer::sum`. – S_K Apr 02 '20 at 13:28
77

The simplified Java 8 way:

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

This uses the method of HashMap that retrieves the value for a key, but if the key can't be retrieved it returns the specified default value (in this case a '0').

This is supported within core Java: HashMap<K,V> getOrDefault(Object key, V defaultValue)

Christopher Bull
  • 2,489
  • 1
  • 23
  • 27
54
hashmap.put(key, hashmap.get(key) + 1);

The method put will replace the value of an existing key and will create it if doesn't exist.

shuttle87
  • 15,466
  • 11
  • 77
  • 106
oracleruiz
  • 1,059
  • 1
  • 8
  • 16
  • 71
    No, it doesn't create, it gives `nullPointer Exception`. – smttsp Feb 01 '15 at 09:52
  • 15
    The code is a correct answer for the given question, but was posted a year after the exact same code was posted in the accepted answer. The thing that differentiates this answer is stating put can create a new entry, which it can, but not in this example. If you are using hashmap.get(key) for a non-existent key/value you'll get null and when you attempt to increment, as @smttsp says it will NPE. -1 – Zach Mar 12 '15 at 17:44
  • 13
    This answer is wrong. NullPointerException for non-existing keys – Eleanore Jul 25 '15 at 10:03
  • @smttp NullpointterException only if you didn't initialize the value ( as you know you cannot increment null ) – Mehdi Mar 14 '16 at 19:47
  • Duplication and incorrect explanation... _and will create it if doesn't exist_ You can't do `null + 1` since this will try to unboxe the `null` into an integer to do the increment. – AxelH Mar 16 '17 at 11:22
30

Replace Integer by AtomicInteger and call one of the incrementAndGet/getAndIncrement methods on it.

An alternative is to wrap an int in your own MutableInteger class which has an increment() method, you only have a threadsafety concern to solve yet.

Hernán Eche
  • 6,529
  • 12
  • 51
  • 76
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 37
    AtomicInteger is a Mutable Integer but builtin. I seriously doubt writing your own MutableInteger is a better idea. – Peter Lawrey Nov 11 '10 at 18:51
  • Custom `MutableInteger` is better, as `AtomicInteger` uses `volatile`, which has overhead. I'd use `int[1]` instead of `MutableInteger`. – Oliv Oct 04 '17 at 13:03
  • @Oliv: not concurrent. – BalusC Oct 04 '17 at 18:22
  • @BalusC but still, volatile write is more expensive. It invalidates caches. If it were no difference, all variables would be volatile. – Oliv Oct 06 '17 at 05:20
  • @Oliv: question explicitly mentions hashcode collision, so concurrency is important for OP. – BalusC Oct 06 '17 at 06:06
  • @BalusC hashcode collision is not about concurrency at all. Concurrency is about accessing the value from multiple threads, which is not required here. – Oliv Oct 09 '17 at 10:05
21

One line solution:

map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);
Punktum
  • 211
  • 2
  • 3
19

@Matthew's solution is the simplest and will perform well enough in most cases.

If you need high performance, AtomicInteger is a better solution ala @BalusC.

However, a faster solution (provided thread safety is not an issue) is to use TObjectIntHashMap which provides a increment(key) method and uses primitives and less objects than creating AtomicIntegers. e.g.

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
map.increment("aaa");
Aniket Kulkarni
  • 12,825
  • 9
  • 67
  • 90
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
14

You can increment like below but you need to check for existence so that a NullPointerException is not thrown

if(!map.containsKey(key)) {
 p.put(key,1);
}
else {
 p.put(key, map.getKey()+1);
}
isuru
  • 149
  • 1
  • 5
9

Does the hash exist (with 0 as the value) or is it "put" to the map on the first increment? If it is "put" on the first increment, the code should look like:

if (hashmap.containsKey(key)) {
    hashmap.put(key, hashmap.get(key)+1);
} else { 
    hashmap.put(key,1);
}
sudoBen
  • 87
  • 1
  • 1
9

It may be little late but here are my two cents.

If you are using Java 8 then you can make use of computeIfPresent method. If the value for the specified key is present and non-null then it attempts to compute a new mapping given the key and its current mapped value.

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]

We can also make use of another method putIfAbsent to put a key. If the specified key is not already associated with a value (or is mapped to null) then this method associates it with the given value and returns null, else returns the current value.

In case the map is shared across threads then we can make use of ConcurrentHashMap and AtomicInteger. From the doc:

An AtomicInteger is an int value that may be updated atomically. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.

We can use them as shown:

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]

One point to observe is we are invoking get to get the value for key B and then invoking incrementAndGet() on its value which is of course AtomicInteger. We can optimize it as the method putIfAbsent returns the value for the key if already present:

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

On a side note if we plan to use AtomicLong then as per documentation under high contention expected throughput of LongAdder is significantly higher, at the expense of higher space consumption. Also check this question.

akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
6

The cleaner solution without NullPointerException is:

map.replace(key, map.get(key) + 1);
Sergey Dirin
  • 435
  • 6
  • 12
4

Since I can't comment to a few answers due to less reputation, I will post a solution which I applied.

for(String key : someArray)
{
   if(hashMap.containsKey(key)//will check if a particular key exist or not 
   {
      hashMap.put(hashMap.get(key),value+1);// increment the value by 1 to an already existing key
   }
   else
   {
      hashMap.put(key,value);// make a new entry into the hashmap
   }
}
2
Integer i = map.get(key);
if(i == null)
   i = (aValue)
map.put(key, i + 1);

or

Integer i = map.get(key);
map.put(key, i == null ? newValue : i + 1);

Integer is Primitive data types http://cs.fit.edu/~ryan/java/language/java-data.html, so you need to take it out, make some process, then put it back. if you have a value which is not Primitive data types, you only need to take it out, process it, no need to put it back into the hashmap.

Kreedz Zhen
  • 380
  • 2
  • 12
  • 1
    Thank you for this code snippet, which may provide some immediate help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its educational value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with similar, but not identical, questions. Please [edit] your answer to add explanation, and give an indication of what limitations and assumptions apply. – Toby Speight Jul 26 '17 at 09:28
  • Correction: Integer class is not a Primitive data type, rather it is a wrapper class for the primitive type int. Moreover, thanks to autoboxing post-Java8, there is already an accepted answer here: https://stackoverflow.com/a/4158002/10993032 – mikerover Jan 28 '21 at 17:07
1

Use a for loop to increment the index:

for (int i =0; i<5; i++){
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("beer", 100);

    int beer = map.get("beer")+i;
    System.out.println("beer " + beer);
    System.out ....

}
Leigh
  • 28,765
  • 10
  • 55
  • 103
VanHoutte
  • 27
  • 1
  • 3
    That would just overwrite the Map on each iteration. See Matthew's answer for the correct approach. – Leigh Jun 14 '12 at 21:06
1

There are misleading answers to this question here that imply Hashtable put method will replace the existing value if the key exists, this is not true for Hashtable but rather for HashMap. See Javadoc for HashMap http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put%28K,%20V%29

1

Use Java8 built in fuction 'computeIfPresent'

Example:

public class ExampleToUpdateMapValue {

    public static void main(String[] args) {
        Map<String,String> bookAuthors = new TreeMap<>();
        bookAuthors.put("Genesis","Moses");
        bookAuthors.put("Joshua","Joshua");
        bookAuthors.put("Judges","Samuel");

        System.out.println("---------------------Before----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
        // To update the existing value using Java 8
        bookAuthors.computeIfPresent("Judges", (k,v) -> v = "Samuel/Nathan/Gad");

        System.out.println("---------------------After----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
    }
}
Rajesh
  • 4,273
  • 1
  • 32
  • 33
0

Try:

HashMap hm=new HashMap<String ,Double >();

NOTE:

String->give the new value; //THIS IS THE KEY
else
Double->pass new value; //THIS IS THE VALUE

You can change either the key or the value in your hashmap, but you can't change both at the same time.

Tim Post
  • 33,371
  • 15
  • 110
  • 174