I have a method that returns Optional<Foo>
, and if it's present, I want to use the value to look up a Stream<Bar>
.
Now I know I can do this:
optionalFoo.map(foo->bars(foo)).orElse(Stream.empty());
(I'm using the full form of the mapping function instead of a method reference for illustration.)
And with Java 9+ I can do this:
optionalFoo.stream().flatMap(foo->bars(foo));
But either way the extra step seems pointless. Is there an mapping method that would simply map the optional to a stream, or an empty stream if the optional is not present? Then I could do:
optionalFoo.streamMap(foo->bars(foo));
If there isn't such a method, why not? It could be added as a simple default method on Optional
.
And lastly, if I have to write my own utility method, which of the above approaches is more efficient (seeing that they both provide the same result): mapping to stream or empty stream; or converting to stream and then flat-mapping to a stream? I would assume the former is more efficient, which illustrates why a utility method is needed. (We don't want to retype .orElse(Stream.empty())
all over the place.)
For those of you who need a concrete use case, assume there is a method to return the HTML <head>
element from a DOM tree:
/**
* Finds the {@code <html><head>} element in the HTML namespace.
* @param document The document tree.
* @return A reference to the {@code <html><head>} element if it exists in the tree.
*/
public static Optional<Element> findHtmlHeadElement(@Nonnull final Document document);
Now another method wants to return all the HTML <meta>
elements if they exist. So it wants to search the children of the <head>
element, but only if it exists, of course. (Note that childElementsByNameNS()
is a utility method that retrieves child elements of another element as a Stream<Element>
.)
/**
* Finds the {@code <html><head><meta>} elements in an HTML document.
* @param document The document tree.
* @return A stream of {@code <html><head><meta>} elements if they exist in the tree.
*/
public static Stream<Element> htmlHeadMetaElements(@Nonnull final Document document) {
return findHtmlHeadElement(document).map(headElement ->
childElementsByNameNS(headElement, XHTML_NAMESPACE_URI_STRING, ELEMENT_META))
.orElse(Stream.empty());
}
Why do I need the extra ugly .orElse(Stream.empty())
boilerplate? I would rather have something like this:
/**
* Finds the {@code <html><head><meta>} elements in an HTML document.
* @param document The document tree.
* @return A stream of {@code <html><head><meta>} elements if they exist in the tree.
*/
public static Stream<Element> htmlHeadMetaElements(@Nonnull final Document document) {
return findHtmlHeadElement(document).streamMap(headElement ->
childElementsByNameNS(headElement, XHTML_NAMESPACE_URI_STRING, ELEMENT_META));
}
You might say that "you can't have special mapping methods from Optional
to every other type", but Optional
and Stream
have a special relationship. In fact I sort of think of Optional
almost as a Stream
with zero or one elements. In fact that's exactly what Optional.stream()
gives back. So this is really complementary to Optional.stream()
.