javadictionarylambda

Strange lambda behaviour in Map.computeIfAbsent


I created the following method that adds the given UUID value with the given Integer to the map variable.

final Map<Integer, List<UUID>> map = new TreeMap<>();

private void addToMap(Integer key, UUID value) {
    map.computeIfAbsent(key, val -> new ArrayList<>()).add(value);
}

Normally I was tried to use map.computeIfAbsent(key, value -> new ArrayList<>()).add(value); by passing value parameter to the lambda function, it it throws "Variable 'value' is already defined in the scope" error pointing the "value" in .. value -> ... part.

My updated addToMap method is working, but I think val is pointless. So, how should this method be updated?


Solution

  • Your addToMap() method implies a wrong understanding of the parameters of the computeIfAbsent() method:

    private void addToMap(Integer key, UUID value) {
        map.computeIfAbsent(key, val -> new ArrayList<>()).add(value);
    }
    

    The parameter in val -> new ArrayList<>() has nothing to do with the value that you pass into addToMap()!

    If you look at the JavaDoc for Map.computeIfAbsent() you can see that the second parameter is a Function<? super K,​? extends V> - i.e. a function that takes a key and returns whatever should be stored in the map.

    It would be better to write the addToMap() method as

    private void addToMap(Integer key, UUID value) {
        map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
    }
    

    Note that the answer from toniedzwiedz is dangerous. It only works for a Map<Integer, ?> with keys greater than or equal to zero.

    It is dangerous because it basically replaces k -> new ArrayList<>() with k -> new ArrayList<>(k) - this happens to work for maps with Integer keys and uses the key value to define the initial capacity of the ArrayList.

    It will not work if k is negative (new ArrayList<>(k) will throw an IllegalArgumentException if k is negative).

    It will also not work for a Map<String, UUID> because there is no ArrayList constructor that takes a String argument.