4

I want to save the return value of a method and use it to create a new object with which ill add to a list. Here is the block of code for more clarity:

final List<FooBoo> fooboos = new ArrayList<>();
for (Foo foo : foos) {
    Optional<Boo> boo = generateBoo(foo);
    if (boo.isPresent()) {
        fooboos.add(new FooBoo(foo, boo.get()));
    }
}

I've tried something like this:

fooboos = foos
            .stream()
            .map(f -> generateBoo(f))
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.toList());

But obviously I'm missing something here which is actuallying creating the FooBoo object. How exactly do I do that with the java stream method?

1
  • Do you really want a list, or do you want a Map<Foo,Boo>? If it's the latter, consider using Map.computeIfAbsent to populate the map on-the-fly. Commented Apr 7, 2016 at 22:21

3 Answers 3

6
fooboos = foos
        .stream()
        .map(foo -> generateBoo(foo).map(boo -> new FooBoo(foo, boo))
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

3 Comments

Why not move the .map to after the map(Optional:get)?
Because you need the foo.
@Louis - generateBoo() returns an Optional, and .map(...) will, if the optional has a value, apply the map, returning a new Optional. The .filter and .map then unwraps the FooBoo, if present.
5

Another possible answer is:

fooboos = foos.stream()
              .flatMap(foo -> generateBoo(foo)
                                  .map(boo -> new FooBoo(foo, boo))
                                  .map(Stream::of)
                                  .orElseGet(Stream::empty)
              ).collect(Collectors.toList());

I think in Java 9 we will see a stream method added to Optional. Then we will be able to do:

fooboos = foos.stream()
              .flatMap(foo -> generateBoo(foo).map(boo -> new FooBoo(foo, boo)).stream())
              .collect(Collectors.toList());

9 Comments

Which could also be written .flatMap(foo -> generateBoo(foo).map(boo -> Stream.of(new FooBoo(foo, boo)).orElseGet(Stream::empty)
optional.stream() will be equivalent to optional.map(Stream::of).orElseGet(Stream::empty)
@PaulBoddington I had a hard time making my brain, conditioned by 15+ years of imperative programming, think using functional idioms, but it's coming. You'll probably get used to it.
Your code is fine. My comment is just a hint about how the Java 8 equivalent can look like (close to JB Nizet’s variant).
To make it ready for Java 9, you could add an additional map: .flatMap(foo -> generateBoo(foo).map(boo -> new FooBoo(foo, boo)).map(Stream::of).orElseGet(Stream::empty))
|
1

You need to hold on too the Foo, but you lose it when you map it to the result of generateBoo. If you have some kind of Pair or Tuple so that you can map both the Foo and the Boo into one object, you can then later combine them. I've seen some people use an array of two objects and AbstractMap.SimpleImmutableEntry as a quick and dirty Pair equivalent, both of which are awkward. Whether you use something existing or create your own simple Pair object, without questioning the wisdom of using Optional in this case, you might do this:

fooboos = foos
        .stream()
        .map(f -> new Pair(f, generateBoo(f))
        .filter(p -> p.getV2().isPresent())
        .map(p -> new Pair(p.getV1(), p.getV2().get())
        .map(p -> new Fooboo(p.getV1(), p.getV2()))
        .collect(Collectors.toList());

2 Comments

This solution requires the creation of a Pair class (or using an external library containing one). In this case, it is unnecessary, since generateBoo() returns an optional, and so can be "optionally mapped" directly into a new FooBoo(foo, boo) as shown by @JB. The Pair object to temporarily hold foo and the optional boo is unnecessary.
Yup. That's why I up voted @JB's answer when I saw it after I finished posting mine. Gave myself a head-slap.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.