16

Is there a more elegant way of practically achieving this in Java 8?

list.stream()
    .map(e -> myclass.returnsOptional(e))
    .filter(Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

I'm talking about filter(Optional::isPresent) followed by map(Optional::get), I want to elegantly collect in a list only Optional results which have a value.

Sartorius
  • 499
  • 5
  • 12
ᴘᴀɴᴀʏɪᴏᴛɪs
  • 7,169
  • 9
  • 50
  • 81
  • 8
    It looks fine to me. I'm not sure what you don't like about it. – khelwood Oct 07 '16 at 15:14
  • 1
    If all your Optionals have a result this is not an Optional anymore... instead of returning an optional and checking if isPresent you can return nulls and filter, it'll be one step shorter. `list.stream().map(e -> myclass.returnsObjectOrNull(e)).filter(Objects::nonNull).collect(Collectors.toList())` – Nir Alfasi Oct 07 '16 at 15:15
  • 2
    .map(o -> o.map(Stream::of).orElseGet(Stream::empty)) or see the answers here: http://stackoverflow.com/questions/22725537/using-java-8s-optional-with-streamflatmap – hasan Oct 07 '16 at 15:16
  • 13
    I do know in java9 that optional with have a stream, so rather than `filter` and `map`, you could do `flatMap(Optional::stream)` so if no one has an answer, keep it in mind for java9 – Ash Oct 07 '16 at 15:16
  • @alfasin They don't all have a result – ᴘᴀɴᴀʏɪᴏᴛɪs Oct 07 '16 at 15:19
  • @Ash French That is exactly the kind of thing I was hoping for, it just felt like there would be a better way of doing it. Too bad it's only for Java 9 but will definitely keep in mind! – ᴘᴀɴᴀʏɪᴏᴛɪs Oct 07 '16 at 15:19
  • @khelwood Nothing in particular I don't like about it, it just felt there was a better built-in way, something along the lines of Ash's Java 9 example. Too bad it's not for Java 8 – ᴘᴀɴᴀʏɪᴏᴛɪs Oct 07 '16 at 15:25
  • I understand that, what I'm saying is that instead of returning `Optional.empty()` from `myclass.returnsOptional` and then filter the empty results, you can simply return null and filter by `Objects::nonNull` – Nir Alfasi Oct 07 '16 at 15:30
  • Can returnsOptional method be static? – eGoLai Oct 07 '16 at 15:39
  • 1
    Looks OK (and I have used this). Slight elegance improvement: `.map(myclass::returnsOptional)` instead of lambda – Bohemian Oct 07 '16 at 17:30

2 Answers2

3

In your case you can use one flatMap instead of combinations of map filter and again map. To Do that it's better to define a separate function for creating a Stream: public private static Stream<Integer> createStream(String e) to not have several lines of code in lambda expression.

Please see my full Demo example:

 public class Demo{
    public static void main(String[] args) {
        List<String> list = Arrays.asList("1", "2", "Hi Stack!", "not", "5");
        List<Integer> newList = list.stream()
                .flatMap(Demo::createStream)
                .collect(Collectors.toList());
        System.out.println(newList);
    }

    public static Stream<Integer> createStream(String e) {
        Optional<Integer> opt = MyClass.returnsOptional(e);
        return opt.isPresent() ? Stream.of(opt.get()) : Stream.empty();
    }
}


class MyClass {
    public static Optional<Integer> returnsOptional(String e) {
        try {
            return Optional.of(Integer.valueOf(e));
        } catch (NumberFormatException ex) {
            return Optional.empty();
        }
    }
}

in case returnsOptional cannot be static you will need to use "arrow" expression instead of "method reference"

eGoLai
  • 360
  • 1
  • 3
  • 16
-1

Not sure if its so different but you could just filter based on your optional instead of getting the optional and filtering next. Something like this?

list.stream()
    .filter(e -> myclass.returnsOptional(e).isPresent())
    .collect(Collectors.toList());

Note: This will only work if returnsOptional returns the same object type as your original list item types.

Amin J
  • 1,179
  • 9
  • 16