is there a better approach to get any value from a nested stream?
String a = obj.getCustomer().getDetails().stream()
.findAny()
.map(CustomerDetail::getContact)
.flatMap(o -> o.getMobiles()
.stream()
.findAny()
.map(o2 -> o2.getNumber()))
.orElse(null);
This approach results in an error - cannot access o as String, it is still a Set<String> despite using findAny.
String b = obj.getCustomer().getDetails().stream()
.findAny()
.map(CustomerDetail::getContact)
.map(Contact::getMobiles)
.stream()
.findAny()
.map(o -> o.getNumber()) // ERROR
.orElse(null);
So if you want to find any phone number of any customer the first code is perfectly fine.
However if you prefer the second variant I can help with that. The problem is you are assuming wrong datatypes. For clarity I added the type to each line of your code:
String b = obj.getCustomer().getDetails().stream() //Stream<CustomerDetail>
.findAny() //Optional<CustomerDetail>
.map(CustomerDetail::getContact) //Optional<Contact>
.map(Contact::getMobiles) //Optional<Set<String>>
.stream() //Stream<Set<String>> Mistake right here!
.findAny() //Optional<Set<String>>
.map(o -> o.getNumber()) // o must be Set<String>!
.orElse(null);
(I am assuming your Java-8 Tag is wrong since you used the stream()-method of the Optional which exists only since Java 9 : https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html#stream())
What is does is basically turn your Optional into a Stream with only one (or zero) elements containing only the element of the Optional.
What you need to do to fix this problem is the following:
String b = obj.getCustomer().getDetails().stream() //Stream<CustomerDetail>
.findAny() //Optional<CustomerDetail>
.map(CustomerDetail::getContact) //Optional<Contact>
.map(Contact::getMobiles) //Optional<Set<String>>
.stream() //Stream<Set<String>>
.flatMap(Collection::stream) // Stream<String> new flat map to turn into a Stream of Strings
.findAny() //Optional<String>
.map(o -> o.getNumber()) // o is String and compiles
.orElse(null);
Edit:
Basically what the change does is to simply 'unpack' the Set.
Your code was basically a Stream that had one single Set inside. Calling Collection::stream transforms the Set into a Stream (Using the inherited stream()-Method from the interface Collection). Now the stream would have another stream inside which contains all the mobiles. That's why it has to be used with flatMap instead of map, which basically unpacks the second stream into the outer one. This results into having all Mobiles of one customer inside the stream now.
If you like this code is better is now on you to decide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With