I wrote recently about the new niceties in the Map
interface that Java 8 brought to light where I’m highlighting in particular 2 new methods: getOrDefault
and computeIfAbsent
(see my previous post here about it). These provide a cleaner (and as it turns out faster too!) way of retrieving values from a Map
instance. However, there is a subtle difference about them when it comes to hitting a key which doesn’t have a value associated in the given Map
, and this is why I thought to write this post.
As a reminder, before these 2 methods became available in Java 8, this is the sort of code we used to write in order to deal with the case when our key is not present in the Map
(I am using here an instance which maps strings to strings for simplicity of code, but the same applies to any other map):
Map<String, String> map = ... String value = map.get("some key"); if( value == null ) { // do something if key not found ... } |
Typically inside the if()
above there are 2 use cases:
- use a “default” value if the key not found in the Map — but don’t store this value in the
Map
- use a “default” value AND store it back in the
Map
under the given key
It turns out that depending on 1 or 2, it matters a lot if we should use getOrDefault
or computeIfAbsent
! The crucial difference lies in the JavaDoc for computeIfAbsent
which states:
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.
Which means that computeIfAbsent
actually stores back the value back in the Map
(if not null
) — thus being equivalent to this code:
Map<String, String> map = ... String value = map.get("some key"); if( value == null ) { value = "some new string"; map.put( "some key", value ); } |
The following piece of code actually showcases this, first using getOrDefault()
:
static final String NOT_FOUND = "not found"; Map<String, String> map = ... String value = map.getOrDefault("some key", NOT_FOUND); //... do some processing // now check if our key exists in the Map System.out.println( map.containsKey("some key") ); // prints "false" |
And using computeIfAbsent()
:
static final String NOT_FOUND = "not found"; Map<String, String> map = ... String value = map.computeIfAbsent("some key", (k) -> NOT_FOUND); //... do some processing // now check if our key exists in the Map System.out.println( map.containsKey("some key") ); // prints "true" |
At first glance they both achieve the same thing: if the value is not in the Map
, they both return a constant (NOT_FOUND
), however, the side effects are different — one modifies the Map
and one doesn’t.
Subtle I’d argue but a rather huge difference.