Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map getOrDefault VS getOrUseSupplier

I am starting to learn lambdas and i do not understand why java Map has:

getOrDefault(Object key, V defaultValue)

and not(working just the same, but if value is absent, then defaultValue will be taken from supplier):

getOrUseSupplier(Object key, Supplier<V> defaultValue)

Adventages i currently see of current solution:

  • defaultValue does not have to be a final/effectively final value
  • looks simpler & no need to know lambda syntax

Disadventages:

  • If while using getOrDefault we put new object creation, it will be created and passed to GC immidietely(while using supplier, it will not be created at all).

I would like to know if there is any more disadventages of using & having getOrDefault rather than getOrUseSupplier. Could you also tell me if anywhere in java libraries, there is method like this:

static <V> V getOrUseSupplier(Map<?, V> map, Object key, Supplier<V> supplier)

that tries to take Value from map, and if it does not exist then takes value from Supplier.

like image 781
user3009344 Avatar asked Apr 20 '16 11:04

user3009344


People also ask

What is map getOrDefault?

The Java HashMap getOrDefault() method returns the specified default value if the mapping for the specified key is not found in the hashmap. Otherwise, the method returns the value corresponding to the specified key. The syntax of the getOrDefault() method is: hashmap.get(Object key, V defaultValue)

What does getOrDefault do in Java?

The getOrDefault(key, defaultValue) method of Properties class is used to get the value mapped to this key, passed as the parameter, in this Properties object. This method will fetch the corresponding value to this key, if present, and return it. If there is no such mapping, then it returns the defaultValue.

How do I find the size of a map?

Map size() Method in Java With Examples Map size() method in Java is used to get the total number entries i.e, key-value pair. So this method is useful when you want total entries present on the map. If the map contains more than Integer. MAX_VALUE elements return Integer.

What are maps that link a key to a value?

Interface Map<K,V> An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value. This interface takes the place of the Dictionary class, which was a totally abstract class rather than an interface.


3 Answers

Objects.requireNonNullElseGet() is a nice way to achieve the desired getOrUseSupplier(Object key, Supplier<V> defaultValueSupplier) behavior.

Objects.requireNonNullElseGet(map.get(key), defaultValueSupplier));

Advantages (comparing to other solutions):

  • one-liner
  • has no side effects on map
  • doesn't create any garbage objects (like Optional)
  • core java (no libraries required)

Requires Java 9.

I'd be happy to have this convenient method getOrUseSupplier() on map itself too. Maybe in upcoming releases...

UPD

I think that this mail from Brian Goetz is the official answer to your original question. Java designers are afraid that programmers will abuse capturing lambdas using this method.

like image 152
Evgeny Kharitonov Avatar answered Oct 04 '22 16:10

Evgeny Kharitonov


When using the getOrDefault() method you should keep in mind that if your second argument (the default value) throws an exception for some reason, you will get that exception thrown even if the key exists in the map, as the default value is evaluated regardless of the existence of the key. Here is an example:

private static final Map<String, Integer> RANKS = Map.of(
        "A", 9,
        "B", 8,
        "C", 7,
        "D", 6
);

public static void main(final String[] args) {
    final int value1 = RANKS.getOrDefault("E", 5); // This will work
    final int value2 = RANKS.getOrDefault("A", Integer.valueOf("G")); // Exception!
}

As you can see even though the key A exists, it will still try to evaluate the default value anyway, which itself throws a NumberFormatException in this example.
If you use that hypothetical getOrUseSupplier() method (which unfortunately, as @Kayaman already explained, doesn't yet exist natively in Java, and you should implement it yourself), you can do this:

final int value = getOrUseSupplier(RANKS, "A", () -> Integer.parseInt("G")); // This will work

Now the exception will be thrown only if the key A is missing.

like image 35
Radoslav Stoyanov Avatar answered Oct 04 '22 16:10

Radoslav Stoyanov


The closest equivalent of getOrUseSupplier() in Map is named computeIfAbsent() which allows for the value to be computed using the key, giving more flexibility than if it only took a Supplier. It also stores the computed value in the Map, unlike getOrDefault. This is because they have distinct use cases and are not really related. While getOrDefault is usually used to return a "safe" non-null default value (such as returning empty string instead of a null) indicating that something should be in the map, computeIfAbsent() implies that something must be in the map, and if it's not, it needs to be created or otherwise the internal state of the program is not correct.

The following example ignores the key and just uses the supplier's value.

public static <V,T> V getOrUseSupplier(Map<T, V> map, T key, Supplier<V> supplier) {
    return map.computeIfAbsent(key, k -> supplier.get());
}
like image 29
Kayaman Avatar answered Oct 04 '22 16:10

Kayaman