I have following Groovy script:
mymap = ['key': 'value']
println mymap
v = mymap.get('notexistkey', 'default')
println v
println mymap
When I run it I get following console output:
[key:value]
default
[key:value, notexistkey:default]
I'm surprised that after calling mymap.get('notexistkey', 'default')
where second parameter is a default value returned when given key does not exist, the key notexistkey
is added to the map I've called the method on. Why? Is that expected behaviour? How can I prevent this mutation?
Use Java's Map.getOrDefault(key, value)
instead:
mymap = ['key': 'value']
println mymap
v = mymap.getOrDefault('notexistingkey', 'default')
println v
println mymap
Output:
[key:value]
default
[key:value]
Groovy SDK adds Map.get(key, default)
via DefaultGroovyMethods.get(map, key, default)
and if you take a look what Javadoc says you will understand that this behaviour is expected:
Looks up an item in a Map for the given key and returns the value - unless there is no entry for the given key in which case add the default value to the map and return that.
And this is what implementation of this method looks like:
/**
* Looks up an item in a Map for the given key and returns the value - unless
* there is no entry for the given key in which case add the default value
* to the map and return that.
* <pre class="groovyTestCase">def map=[:]
* map.get("a", []) << 5
* assert map == [a:[5]]</pre>
*
* @param map a Map
* @param key the key to lookup the value of
* @param defaultValue the value to return and add to the map for this key if
* there is no entry for the given key
* @return the value of the given key or the default value, added to the map if the
* key did not exist
* @since 1.0
*/
public static <K, V> V get(Map<K, V> map, K key, V defaultValue) {
if (!map.containsKey(key)) {
map.put(key, defaultValue);
}
return map.get(key);
}
it's pretty old concept (since Groovy 1.0). However I would recommend not using it - this .get(key, default)
operation is neither atomic, nor synchronized. And problems start when you use it on ConcurrentMap
which is designed for concurrent access - this method breaks its contract, because there is no synchronization between containsKey
, put
and final get
call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With