Why not simply:
Copyreturn Optional.ofNullable(myMap.get(myKey));
JavaDocs
Answer from Eugene on Stack Overflowjava - How to make `Map::get` return either an `Optional` of the found value or `Optional.empty()` - Stack Overflow
java - Use of Optional in a map - Stack Overflow
java - How does Optional.map() exactly works? - Stack Overflow
option type - Existing way to Java map Optional<> onto object instead of to another object? - Stack Overflow
Videos
Why not simply:
Copyreturn Optional.ofNullable(myMap.get(myKey));
JavaDocs
This
CopyOptional.of(myMap.getOrDefault(myKey, null));
or really
CopyOptional.of(null);
would've failed with a NullPointerException. As the javadoc states
Throws:
NullPointerException - ifvalueis null
Optional#ofNullable exists when you don't know if the value you're passing to it is null or not:
Parameters:
value- the possibly-null value to describe
And since Map#get(Object) already returns null when there is no entry for the given key
Returns:
the value to which the specified key is mapped, ornullif this map contains no mapping for the key
you don't need to use getOrDefault with a null value for the default. You can instead directly use
CopyOptional.ofNullable(myMap.get(myKey));
The whole chain of operations returns a String:
- The first step (
map(...)) maps theOptional<User>to anOptional<String>. - The second step (
orElseThrow(...)) unwraps theOptional<String>, thus returning aString(or throwing anIllegalArgumentException, if empty).
We can find the source code of Optional::map here.
You are totally correct. The map() method returns an Optional, and I applaud your use of the javadoc. The difference here is that you then invoke the orElseThrow() method on that Optional that map() returned. If you refer to the javadoc for orElseThrow(), you will see that it returns "the present value [of the Optional]". In this case, that is a String.
Given the example you've provided, it seems that you want to apply the operation foo.add if bar is present otherwise return foo:
Foo foobar = bar.isPresent() ? foo.add(bar.get()) : foo;
Thus, I'd say it's so short that it's not worthy of creating a utility method for it.
Foo foobar = optBar.map(f::add).orElse(f); // map to Foo if bar is present else return `f`
- if
optBarhas a present state, we apply themapoperation andorElsereturns that mapped value. - if
optBarhas an absent state, theorElsemethod returns the supplied default valuef.
Another thing, are you getting the result as an Optional<Bar> in the first place or are you manually wrapping Bar into an Optional? if it's the latter I'd probably just use a simple if statement which would be the ideal solution, i.e.
Foo foo = new Foo();
if(bar != null) foo = foo.add(bar);
Using bar rather than foo seems to simplify it:
Foo foobar = bar
.map(b -> foo.map(f -> f.add(b)))
.orElse(foo)
.orElse(null); // if foo is not present
bar.map(b -> foo.map(f -> f.add(b))) returns Optional<Optional<Foo>> that is "present" if bar is present.
.orElse(foo) reverts to the original Optional<Foo> if bar was not present (or returns foo.map(f -> f.add(b)) otherwise).
Use map if the function returns the object you need or flatMap if the function returns an Optional. For example:
public static void main(String[] args) {
Optional<String> s = Optional.of("input");
System.out.println(s.map(Test::getOutput));
System.out.println(s.flatMap(Test::getOutputOpt));
}
static String getOutput(String input) {
return input == null ? null : "output for " + input;
}
static Optional<String> getOutputOpt(String input) {
return input == null ? Optional.empty() : Optional.of("output for " + input);
}
Both print statements print the same thing.
They both take a function from the type of the optional to something.
map() applies the function "as is" on the optional you have:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
What happens if your function is a function from T -> Optional<U>?
Your result is now an Optional<Optional<U>>!
That's what flatMap() is about: if your function already returns an Optional, flatMap() is a bit smarter and doesn't double wrap it, returning Optional<U>.
It's the composition of two functional idioms: map and flatten.
Here's an example of pure stream solution (without ifs and ternary operators)
final Map<MyCoolKey, Optional<MyCoolValue>> myCoolMap = new HashMap<>();
Optional<Map<MyCoolKey, MyCoolValue>> output = Optional.of(myCoolMap)
.filter(map -> map.values().stream().allMatch(Optional::isPresent))
.map(map -> map
.entrySet()
.stream()
.collect(toMap(
Map.Entry::getKey,
entry -> entry.getValue().get()
))
);
It's not over-complicating - filtering and mapping are what's streams are for!
The following method does the job:
Map<MyCoolKey, Optional<MyCoolValue>> input;
Optional<Map<MyCoolKey, MyCoolValue>> output = convertMapWithOptionals(input);
where I came up with two "flavors" of this method:
1) Reluctant: first check all, then start converting
<K, V> Optional<Map<K, V>> convertMapWithOptionals(Map<K, Optional<V>> map) {
if (!map.values().stream().allMatch(Optional::isPresent)) {
return Optional.empty();
}
return Optional.of(map.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey, entry -> entry.getValue().get()
)));
}
2) Eager: start converting and abort when necessary
<K, V> Optional<Map<K, V>> convertMapWithOptionals(Map<K, Optional<V>> map) {
Map<K, V> result = new HashMap<>();
for (Map.Entry<K, Optional<V>> entry : map.entrySet()) {
if (!entry.getValue().isPresent()) {
return Optional.empty();
}
result.put(entry.getKey(), entry.getValue().get());
}
return Optional.of(result);
}