Yves Viegen
Yves Viegen

Reputation: 159

How to get ComputeIfPresent working with Map within Map?

When trying to alter a Map using the computeIfPresent() method I have trouble implementing this method when I use an innerMap.

This works:

Map<String, Integer> mapOne = new HashMap<>();
mapOne.computeIfPresent(key, (k, v) -> v + 1);

This doesn't work:

Map<String, Map<String, Integer>> mapTwo = new HashMap<>();
mapTwo.computeIfPresent(key, (k, v) -> v.computeIfPresent(anotherKey, (x, y) -> y + 1);

In the second example I get the following error message: "Bad return type in lambda expression: Integer cannot be converted to Map<String, Integer>". My IDE recognizes v as a Map. But the function doesn't work.

Apparently the method returns an Integer, but I fail to see how this differs from the first method with no Innermap. So far I haven't found a similar case online.

How can I get this to work?

Upvotes: 16

Views: 7127

Answers (3)

Eugene
Eugene

Reputation: 120968

Slightly off-topic (sort of), but doing the same thing for the same Map, breaks in mysterious ways:

// breaks with ConcurrentModificationException
Map<String, Integer> test = new HashMap<>(); // or  = new Hashtable<>();
test.computeIfAbsent("one", x -> test.computeIfAbsent("one", y -> 1));

// IllegalStateException: Recursive update
Map<String, Integer> test = new ConcurrentHashMap<>();
// ... same code

// the only one that works correctly that I am aware of
Map<String, Integer> test = new ConcurrentSkipListMap<>();

Upvotes: 0

FilipRistic
FilipRistic

Reputation: 2821

In order to understand your problem lets take a look at the signature of the method you are using:

V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

As you can see, it returns a type based on the V generic parameter which stands for a value that is stored in Map. This is where you face the problem: your inner map stores Integer, so when you call computeIfPresent, you get Integer while your outer map requires another Map.

EDIT:
While writing, I realized that Eran gave a code example already which shows how to do it.
But I will leave this answer since it explains why your approach doesn't work, so it might help someone.

Upvotes: 9

Eran
Eran

Reputation: 393936

The outer lambda expression should return the Map referenced by v:

mapTwo.computeIfPresent(key, 
                        (k, v) -> {
                                v.computeIfPresent(anotherKey, (x, y) -> y + 1); 
                                return v;
                            });

It cannot return the Integer value of the expression v.computeIfPresent(anotherKey, (x, y) -> y + 1);.

Upvotes: 11

Related Questions