Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redundant assignment in Map's putIfAbsent implementation

Tags:

java

Looking at the implementation of the default method putIfAbsent in the interface Map,

default V putIfAbsent(K key, V value) {
    V v = get(key);
    if (v == null) {
        v = put(key, value);
    }

    return v;
}

I wonder why the assignment

v = put(key, value);

was done there instead of simply discarding the returned value? This assignment seems unnecessary because v is already null, and that is what the put method, according to its contract, always returns in this case.

like image 619
John McClane Avatar asked Nov 19 '25 02:11

John McClane


2 Answers

The implementation is like that because the javadoc states it should be:

Implementation Requirements:

The default implementation is equivalent to, for this map:

 V v = map.get(key);
 if (v == null)
     v = map.put(key, value);

 return v;

But why is it specified like that? Probably to get reasonably well-defined behavior in the presence of race conditions that cannot be addressed1.

First, here's what the javadoc says about it.

The default implementation makes no guarantees about synchronization or atomicity properties of this method. Any implementation providing atomicity guarantees must override this method and document its concurrency properties.

So how will the above implementation behave?

  1. If there is no race with another thread mutating that map entry, it will either return null or the previous value (without updating the map).

  2. If there is a race and another thread updates the entry immediately after the get call, then the map.put call will NOT return null. Instead, it will actually return the value that the other thread inserted. And that value will be returned to the caller.

Note that this is not exactly in conformance with the primary description for the putIfAbsent method. But this is excused by the "no guarantees" statement. And furthermore, it kind of makes sense.

Is this useful? Well ... probably yes, if the actual Map method is thread safe. Because, if the calling code wanted to look, this would allow it to detect that a race had occurred and (if it made sense in the context of the overall application design) try to do something about it.

1 - The race conditions cannot be addressed in a default method which has no knowledge of the behavior of implementation classes. They could be addressed in the implementation classes themselves ... by overriding the method to have atomic properties, for example. And the method spec in the Map javadocs clearly flag this possibility.


But the bottom line is that that supposedly redundant assignment is there because the spec clearly says that it should be there.

like image 63
Stephen C Avatar answered Nov 20 '25 17:11

Stephen C


It must always return the old value.

If the map is accessed by multiple threads, the value returned by put may differ from the value returned by get, so assigning to v ensures better behavior.

Of course, the code isn't great for handling multi-threaded access, so any valid thread-safe implementation overrides the method to do it better, but this is the best that a generic default implementation can achieve.

like image 24
Andreas Avatar answered Nov 20 '25 15:11

Andreas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!