20

Having an Optional List of Optional's like:

Optional<List<Optional<String>>> optionalList = Optional.of(
    Arrays.asList(
        Optional.empty(),
        Optional.of("ONE"),
        Optional.of("TWO")));

How to traverse optionalList to print out the string's ONE and TWO ?

What about having an Optional Stream of Optionals?

Optional<Stream<Optional<String>>> optionalStream = Optional.of(
    Stream.of(
        Optional.empty(),
        Optional.of("ONE"),
        Optional.of("TWO")));

Update: Thanks for answers, solution for optionalStream (non nested):

optionalStream
    .orElseGet(Stream::empty)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .forEach(System.out::println);
Didier L
  • 18,905
  • 10
  • 61
  • 103
Sara Vino
  • 203
  • 1
  • 2
  • 5
  • 1
    What have you tried so far? – Ralf Renz Aug 03 '18 at 08:22
  • You can also just map the `Optional` to a `Stream` instead of checking `isPresent` and getting the value yourself explicitly. Will be a bit more 'functional style'. – jbx Aug 03 '18 at 09:12
  • You should avoid storing `Optional` in collections, see [Uses for Optional](https://stackoverflow.com/questions/23454952/uses-for-optional) and [Is it Worth it to Use 'Optional' in Collections?](https://dzone.com/articles/is-it-worth-it-to-use-optional-in-collections) – Didier L Aug 03 '18 at 14:04
  • @DidierL true, however this is still a valid exercise for the case of `Stream>` which is much more common when the items in your collection are being mapped to an `Optional` by some other function, so you end up with a `Stream>`, which you then want to filter and convert to a `Stream`. – jbx Aug 03 '18 at 15:33
  • Indeed, for me this was a valid case as well. Note that there is an [Optional.stream()](https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html#stream--) method introduced in Java 9 to avoid that (to combine with `flatMap()`). Finally, I would also avoid `Optional` and `Optional` as they are not very friendly to work with. Use empty lists/streams instead whenever possible as this makes far cleaner API's. – Didier L Aug 03 '18 at 16:02
  • Yes true. (Refer to my answer about Java 9). Yes having an `Optional` is not really considered good practice... ideally one returns an empty `List`, however I did encounter some cases where there was a difference between returning an empty list and not returning one at all. (Traditionally it would be returning a `null` instead of empty list.) Ideally one avoids this because it is confusing to the reader, but sometimes situations do arise which put you in a corner. – jbx Aug 06 '18 at 08:23

6 Answers6

13

First, check if the Optional is present. If yes, then stream the list and filter the non-empty ones and print each of them.

optionalList.ifPresent(list -> list.stream()
            .filter(Optional::isPresent)
            .map(Optional::get)
            .forEach(System.out::println));

Almost similar for the stream case too

optionalStream.ifPresent(stream -> stream
            .filter(Optional::isPresent)
            .map(Optional::get)
            .forEach(System.out::println));
Thiyagu
  • 17,362
  • 5
  • 42
  • 79
8

You can indeed stream the Option<String> and filter only non empty values.

Optional<List<Optional<String>>> optionalList = Optional.of(Arrays.asList(Optional.empty(), Optional.of("ONE"), Optional.of("TWO")));

optionalList.orElseGet(ArrayList::new)
            .stream()
            .filter(Optional::isPresent)
            .map(Optional::get)           
            .forEach(System.out::println);

You can also use Optional.ifPresent() as suggested in another answers :

optionalList.ifPresent(l -> l.stream()
                             .filter(Optional::isPresent)
                             .map(Optional::get)                               
                             .forEach(System.out::println));

Personally I prefer the first way because it removes a nested level : I find it more pleasant to read.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • The first way will create an empty `ArrayList` for nothing if the list does not exist. – jbx Aug 03 '18 at 09:17
  • It is right. But creating an empty `ArrayList` living in the scope of a stream execution costs also nothing. – davidxxx Aug 03 '18 at 09:25
  • I don't have an IDE handy, but wouldn't a `flatMap` squash the need for all of the `isPresent` checks? – chrylis -cautiouslyoptimistic- Aug 03 '18 at 10:18
  • @chrylis Yes. All these answers are so bloated, when all one needs to do is convert the `Optional` items in the list to a `Stream` and `flatMap` to it. – jbx Aug 06 '18 at 08:26
8

If you can use Java 9, it can be done like this:

optionalList.ifPresent(list -> list.stream()
  .flatMap(Optional::stream)
  .forEach(System.out::println));

For a stream of optionals it would be the same, without the first .stream() call.

With Java 8 you don't have the Optional::stream method available so you can do it yourself:

optionalList.ifPresent(list -> list.stream()
  .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
  .forEach(System.out::println));

And for a Stream of Optionals it would look like this:

optionalStream.ifPresent(stream -> stream
  .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
  .forEach(System.out::println));
jbx
  • 21,365
  • 18
  • 90
  • 144
  • tried for a stream of optionals omitting first `.stream()` call - but not working (java 8) – Sara Vino Aug 03 '18 at 09:48
  • What do you mean doesn't work? Just tried the Java 8 version, with the first line replaced to: `optionalStream.ifPresent(stream -> stream` definitely works. – jbx Aug 03 '18 at 15:29
4
optionalList.stream().flatMap(List::stream).filter(Objects::nonNull).forEach(...)
A. S. Ranjan
  • 355
  • 4
  • 4
  • This version is nice because it avoids the creation of an empty list and skips right to the empty stream (but is only possible in Java 9+ when `Optional.stream` was introduced). – Rangi Keen Mar 03 '23 at 23:10
2

Well ...

  1. Check whether the optional list is present.
  2. Do a "for each" for all elements of the (now present) list.
  3. In each step check whether the optional string is present.
  4. If yes, print it.

A one-liner can do that:

optionalList.ifPresent(list -> list.forEach(s -> s.ifPresent(System.out::println)));
Seelenvirtuose
  • 20,273
  • 6
  • 37
  • 66
2

As I see there are two ways, second one look a bit more pretty to me, take a look:

class Scratch {
    public static void main(String[] args) {
        Optional<String> element1 = Optional.of("test1");
        Optional<String> element2 = Optional.empty();
        Optional<String> element3 = Optional.of("test2");
        Optional<String> element4 = Optional.of("test3");
        List<Optional<String>> list = Arrays.asList(element1, element2, element3, element4);

        System.out.println(extractStrings1(list));
        System.out.println(extractStrings2(list));
    }

    private static List<String> extractStrings1(List<Optional<String>> list) {
        return list.stream()
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
    }

    private static List<String> extractStrings2(List<Optional<String>> list) {
        List<String> result = new ArrayList<>();
        list.forEach(element -> element.ifPresent(result::add));
        return result;
    }
}
Rostislav V
  • 1,706
  • 1
  • 19
  • 31