0

Suppose I have this class model hierarchy:

public class A {
 private Integer id;
 private List<B> b;
}

And:

public class B {
 private Integer id;
 private List<C> c;
}

And finally:

public class C {
 private Integer id;
}

And a simple Service:

@Service
public class doSome {
  
  public void test() {
    Optional<A> a = Optional.of(a) // Suppose a is an instance with full hierarchy contains values
    /** *1 **/               // what I want to do
  }
}

Now what I want to do at the *1 position is to use lambda to extract the Optional value (if exixsts) and map the subrelation to obtain all id of the C class. I have tried something like this:

  public void test() {
    Optional<A> a = Optional.of(a);
    List<Integer> temp = a.get().getB()
                                .stream()
                                .map(x -> x.getC())
                                .flatMap(List::stream)
                                .map(y -> y.getId())
                                .collect(Collectors.toList());   // works
  }

Now I would like to put inside my lambda the a.get().getB(), I have tried several ways but with no luck.

Anyway I don't understand why I can't use two consecutive map like

 .map(x -> x.getC())
 .flatMap(List::stream)
 .map(y -> y.getId())

without using flatMap(List::stream) in the middle... the map doesn't return a new Stream of Type R (class C in this case)? Why I have to Stream it again? where am I wrong?

----------------------- UPDATE ------------------

This is just an example, It's pretty clear that the Optional here is useless but in real case could comes by a findById() JPA Query.

Holger for this reasons I would put all inside a part of code, doing something like:

public <T> T findSome(Integer id) {
  Optional<T> opt = repository.findById(id);
  return opt.map(opt -> opt).orElse(null);
}

I have read here some solution like follows:

Optional.ofNullable(MyObject.getPeople())
  .map(people -> people                                                                    
    .stream()                                                                    
    .filter(person -> person.getName().equals("test1"))
    .findFirst()
    .map(person -> person.getId()))
  .orElse(null);

And I would like to adapt at my case but without no luck.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
CoderJammer
  • 599
  • 3
  • 8
  • 27
  • 2
    1) it’s not clear why you create this superfluous `Optional` at all, then 2) it’s not clear what “I would like to put inside my lambda the `a.get().getB()`” is supposed to mean. Which lambda? And why do you want to put that expression inside it when you already have code that works fine? 3) See [What's the difference between map() and flatMap() methods in Java 8?](https://stackoverflow.com/q/26684562/2711488) or in short, can you invoke `getId()` on a `List`? Or do you need a construct to tell that you want to do it for every element (e.g. by streaming over it)? – Holger May 25 '21 at 11:02
  • 1
    When you invoke a method that returns an optional on purpose, i.e. because it might return an empty optional you should not perform an unconditional invocation of `get` on it. – Holger May 25 '21 at 11:19
  • You have to use flatMap because you have a List of Lists (every B has a List of C and you have many Bs). – HectorLector May 25 '21 at 13:48

1 Answers1

1

As of and newer, you can call Optional#stream:

List<Integer> temp = a.map(A::getB)
        .stream()
        .flatMap(Collection::stream)
        .map(B::getC)
        .flatMap(Collection::stream)
        .map(C::getId)
        .collect(Collectors.toList());

If you are stuck with , you need to map to Stream (or return the empty one) and continue chaining:

List<Integer> temp = a.map(A::getB)
        .map(Collection::stream)
        .orElse(Stream.empty())
        .map(B::getC)
        .flatMap(Collection::stream)
        .map(C::getId)
        .collect(Collectors.toList());

Note: Optional<A> a = Optional.of(a) is not valid as a is already defined.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183