Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Java 8 Optional for safe Map traversal

I have Map<String, Map<String, String>> myMap in my Java 8 class. I need to navigate to a leaf String like myMap['keyA']['keyB'], returning null if either 'keyA' or 'keyB' does not exist in the correlating Map.

In groovy I would use myMap?.keyA?.keyB and be done with it. I understand that Java 8's Optional<T> brings similar behavior into java. Is there a way to use this new behavior to concisely mimic the groovy functionality? If not, is there another concise way to get this behavior in Java 8, or am I still stuck with elaborate procedural code?

like image 229
therealmitchconnors Avatar asked Aug 12 '15 18:08

therealmitchconnors


2 Answers

String valueOrNull = Optional.ofNullable(myMap.get("keyA"))
                             .map(x -> x.get("keyB"))
                             .orElse(null);

First, it wraps the results of the first lookup in an Optional, which acts as a monad. If you add a third layer (myMap.?keyA.?keyB.?keyC), it would look like this:

String valueOrNull = Optional.ofNullable(myMap.get("keyA"))
                             .map(x -> x.get("keyB"))
                             .map(x -> x.get("keyC"))
                             .orElse(null);
like image 148
Philipp Claßen Avatar answered Oct 07 '22 13:10

Philipp Claßen


You can use Optional's ofNullable method to create an Optional that may or may not represent a null value. Then you can use the map method that will, with a Function, map the result to a new value if the value wasn't already null.

Here, I supply a Function as a lambda expression to get the value from the second Map using the second key.

Optional<String> result = Optional.ofNullable(myMap.get("keyA")).map(m -> m.get("keyB"));

From there you can see if the Optional has a value with isPresent(), and if so, get it with get().

Testing:

public static Optional<String> method(Map<String, Map<String, String>> map,
    String key1, String key2)
{
    return Optional.ofNullable(map.get(key1)).map(m -> m.get(key2));
}

Calling Code:

Map<String, Map<String, String>> myMap = new HashMap<>();
Map<String, String> inner = new HashMap<>();
inner.put("one", "two");
myMap.put("three", inner);
System.out.println(method(myMap, "three", "one"));
System.out.println(method(myMap, "three", "dne"));
System.out.println(method(myMap, "dne", "dne"));

Output:

Optional[two]
Optional.empty
Optional.empty
like image 4
rgettman Avatar answered Oct 07 '22 14:10

rgettman