Home > Software design >  Java HashMap computeIfAbsent Illegal Capacity
Java HashMap computeIfAbsent Illegal Capacity

Time:02-28

I have a hashMap with an arrayList as its value. I would like to use the computeIfAbsent method to efficiently create the list when a key is not in the map. Then I tried to use ArrayList::new instead of k -> new ArrayList<Integer>() to make the code more clear, but got "invalid capacity" error if the key is a negative number.

Map<Integer, List<Integer>> map = new HashMap<>();

map.computeIfAbsent(-4, x -> new ArrayList<Integer>());//This is OK
map.computeIfAbsent(-5, ArrayList::new);//This gives error
java.lang.IllegalArgumentException: Illegal Capacity: -5

Looks like the key is passed to the constructor. Could anyone tell me how this happens? Is it possible I can still use method reference in this case to create the arrayList?

CodePudding user response:

This is how the computeIfAbsent method defined:

From the class java.util.HashMap source code:

@Override
public V computeIfAbsent(K key,Function<? super K, ? extends V> mappingFunction) {

  ... // some code
  V v = mappingFunction.apply(key); // create value
  // ... put value into the map itself
}

So we're talking about the mappingFunction here: it has to accept one parameter which is in "runtime" is a key (the first parameter to computeIfAbsent)

So Java "matches" this Function to the constructor of ArrayList that accepts one (int) parameter here, because this is how method inference works, otherwise it won't even compile.

Now, here is a constructor of java.util.ArrayList with one int parameter:

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: " 
                                               initialCapacity);
        }
    }

Since the key is "-5" in your example, this constructor is called with "-5" at the end of the day. It enters the "else" block and hence the exception...

When you use the first construction:

map.computeIfAbsent(-4, x -> new ArrayList<Integer>());

"x" will resolve to -4 but its ok because you don't pass into the constructor of ArrayList, or, in other words you don't use it in the mapping function.

  • Related