4

I have the following list and use LinkedHashMap.

I want to increase the value of the key by 1 (if the key is not present in the map, it starts from 0, and I add +1):

int nums[] = new int[]{4, 10, 5, 4, 2, 10};

Map<Integer, Integer> map = new LinkedHashMap<>();

for (int i = 0; i < nums.length; i++) {
    int key = nums[i];
    int value = map.getOrDefault(key, 0) + 1;

    // I want to use merge method, but have no idea 
    // what should be the 3 rd parameter denoted by "<??????>"
    int value = map.merge(key, 1, <??????>)

    map.put(key, value);
}

However, I want to use merge() method, but have no idea what should be the third parameter denoted by "<??????>" (see the code).

So, how can I use merge method in this scenario?

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • Can you explain exactly how `Integer::sum` doesn't work? – Sweeper May 24 '22 at 10:38
  • 1
    Possibly related: [how to easily sum two hashMap?](https://stackoverflow.com/q/33640088) – Pshemo May 24 '22 at 10:39
  • @Sweeper Do you mean `int value = map.merge(key, 1, Integer::sum);` amigo? I tried, but could not understand how it is working in this example. Any clarification pls? –  May 24 '22 at 10:43
  • Ah I think I see where your confusion is. You don't need `int value =` if you just want to update the map. And you don't need `map.put(key, value);`. Just do `map.merge` and that's it. – Sweeper May 24 '22 at 10:44
  • Yeah, exactly. So, could you please explain me how `map.merge(key, 1, Integer::sum);` line works? –  May 24 '22 at 10:47

3 Answers3

2

The third parameter of the merge method is a BiFunction, i.e. a functional interface accepting two parameters of generic type V (the type of your values) and returning the merge of said values.

Assuming that in your code you're trying to get the frequency of each key, although you wrote "it starts from 0 and I add +1", here is an example of what you were attempting.

The merge operation is up to you, in your case it simply consists in keeping the first value (the one already present within your map), incrementing it by one and ignoring the second value. The merge could also simply consist in summing the first and second values as for each key you're just mapping the value 1.

int nums[] = new int[]{4, 10, 5, 4, 2, 10};

Map<Integer, Integer> map = new LinkedHashMap<>();
for (int i = 0; i < nums.length; i++) {
    map.merge(nums[i], 1, (v1, v2) -> v1 + 1);

    //Alternatively summing the two values
    //map.merge(nums[i], 1, (v1, v2) -> v1 + v2);
}

System.out.println(map);

However, if your goal was to get the number of key collisions rather than their frequency then the code above still works by simply replacing the value 1 with 0.

int nums[] = new int[]{4, 10, 5, 4, 2, 10};

Map<Integer, Integer> map = new LinkedHashMap<>();
for (int i = 0; i < nums.length; i++) {
    map.merge(nums[i], 0, (v1, v2) -> v1 + 1);
}

System.out.println(map);
Dan
  • 3,647
  • 5
  • 20
  • 26
  • @stack Did you run the code? A *key* encountered for the firs time (absent in the map) will be associated with `1`, not `0`. – Alexander Ivanchenko May 24 '22 at 11:00
  • @AlexanderIvanchenko I think he wants to get the frequency of each key. Probably when he wrote "it starts from 0 and I add +1" was a typo by judging by his code. – Dan May 24 '22 at 11:02
  • Yeah, I tried and works (I used `map.merge(nums[i], 1, (v1, v2) -> v1 + v2);` way). Thanks a lot amigos. –  May 24 '22 at 11:15
  • @stack good to know. I've also updated the answer in case you wanted to get either the frequency as posted originally or the number of collisions. – Dan May 24 '22 at 11:18
1

If only increment/decrement is needed on the integer values, a more economic approach (fewer instances of Integer created) would be to use a mutable integer class (like AtomicInteger) as the value type.

Increment/Decrement would then be trivial:

Map<String, AtomicInteger> map = new HashMap<>();
// Create a mapping for key "foo" if not exists, then increment
int n = map.computeIfAbsent("foo", (k) -> new AtomicInteger())
    .incrementAndGet(); // or addAndGet() or similar
Michail Alexakis
  • 1,405
  • 15
  • 14
  • @user16320675, i noted that "economic" means fewer instances of `Integer` created. If you use `Integer` as a value type, each update/merge will create a new object. – Michail Alexakis May 24 '22 at 14:31
0

if the key is not present in the map, it starts from 0 and I add +1

In order to start count from zero you can use method compute(), which expects a key and remappingFunction that allow to compute a new value based on the key and existing value:

int nums[] = new int[]{4, 10, 5, 4, 2, 10};
    
Map<Integer, Integer> map = new LinkedHashMap<>();
    
for (int num : nums) {
    map.compute(num, (k, v) -> v == null ? 0 : v + 1);
}
    
System.out.println(map);

Output:

{4=1, 10=1, 5=0, 2=0}
  • keys 4 and 10 - occur twice and therefore will be associated with the value of 1;
  • keys 5 and 2 - will be encountered only once and therefore get mapped to the value of 0 (as it was required for the keys that were encountered for the first time, i.e. not present in the map)

However, I want to use merge()

With map.merge(num, 1, Integer::sum); a key that hasn't been present in the map will be associated with the value of 1 from the beginning. I.e. the remappingFunction provided as the third argument will not be executed and a new entry comprised from the offered key and value would be placed into a map.

And then when such key will be encountered second, third, etc. time the remappingFunction will be used to combine the old value and a new value (1), i.e. value will be incremented by one.

To achieve the same result as shown above with merge(), we can place condition around merge(). Inside a condition, we can use putIfAbsent() to initialize the entry will 0. If the given key wasn't present, putIfAbsent() will return null, otherwise existing value:

int nums[] = new int[]{4, 10, 5, 4, 2, 10};
    
Map<Integer, Integer> map = new LinkedHashMap<>();
    
for (int num : nums) {
    if (map.putIfAbsent(num, 0) != null) {
        map.merge(num, 1, Integer::sum);
    }
}

Output:

{4=1, 10=1, 5=0, 2=0}
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46