Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redesigning method to use Java 8 Map.computeIfAbsent() with thrown exception

Tags:

java

java-8

I am trying to rework some of my methods to make them more concise using Java 8, whose new features I am trying to slowly absorb.

It is a method with the purpose of adding a value to a Map<Key, Set<Value>>. There are three posibilities:

  1. The key doesn't exist: it is added and a new set containing the value is associated to it.
  2. The key exists: the value is added to the existing set. Note that a set will never be null, because I have some preconditions to deal with that.
  3. The key exists and the value is already contained in the set: an IllegalArgumentException is thrown.

The code implementing this behaviour is the following, and it does not use Java 8 features:

public void addValue(Key key, Value value) {
    // irrelevant preconditions...

    Set<Value> valuesForKey = myMap.get(key);
    if (valuesForKey != null && valuesForKey.contains(value))
        throw new IllegalArgumentException("Association exists already");

    if (valuesForKey == null)
        myMap.put(key, new HashSet<Value>(Arrays.asList(value)));
    else
        valuesForKey.add(value);
}

I would like to shorten this code using Java 8 methods like computeIfAbsent.

I could sum up the last if-else block, but I can't get past the redundancy of the value set to which key maps to having already being retrieved when performing the precondition checks.

public void addValue(Key key, Value value) {
    // irrelevant preconditions...

    Set<Value> valuesForKey = myMap.get(key);
    if (valuesForKey != null && valuesForKey.contains(value))
        throw new IllegalArgumentException("Association exists already");

    myMap.computeIfAbsent(key, v -> new HashSet<Value>()).add(value);
}

Is there anyway I could merge all that in one instruction?

like image 250
dabadaba Avatar asked May 17 '16 13:05

dabadaba


People also ask

What does the computeIfAbsent () method on map do?

The computeIfAbsent(Key, Function) method of HashMap class is used to compute value for a given key using the given mapping function, if key is not already associated with a value (or is mapped to null) and enter that computed value in Hashmap else null.

Does computeIfAbsent Add to map?

computeIfAbsent. If the specified key is not already associated with a value (or is mapped to null ), attempts to compute its value using the given mapping function and enters it into this map unless null .

What is the difference between putIfAbsent and computeIfAbsent?

Both functions aspire to add an element if the specified Key is not already present in Map. putIfAbsent adds an element with the specified Value whereas computeIfAbsent adds an element with the value computed using the Key.

Is computeIfAbsent Atomic?

The 'computeIfAbsent(..)' function essentially has 3 steps: (1) see that the value is missing, (2) compute the Function, and (3) store the value in the map. Since this is atomic, there cannot be an interleaving of two threads where A does step 1, then B does 1, then both do steps 2 and then 3.


1 Answers

You can take advantage of the fact that the add(element) method will return true if the set didn't contain the specified element. If this call returns false, it means the element wasn't added because it was already present. Therefore, you can use:

public void addValue(Key key, Value value) {
    boolean added = myMap.computeIfAbsent(key, k -> new HashSet<>()).add(value);
    if (!added) {
        throw new IllegalArgumentException("Association exists already");
    }
}
like image 178
Tunaki Avatar answered Sep 19 '22 05:09

Tunaki