2

I am doing simple test.

@Test
    public void whenFilterEmployees_thenGetFilteredEmployees(){
        Integer[] empIds = {1,2,3};
        List<Optional<Employee>> employees = Stream.of(empIds)
                .map(employeeRepository::findById)
                .collect(Collectors.toList());

        List<Employee> employeeList = employees
                .stream().filter(Optional::isPresent)
                .map(Optional::get)
                .filter(e->e !=null)
                .filter(e->e.getSalary()>200000)
                .collect(Collectors.toList());

        assertEquals(Arrays.asList(arrayOfEmps[2]), employeeList);


    }

and my employee table contains data:

1   Jeff Bezos  100000
2   Bill Gates  200000
3   Mark Zuckerberg 300000

the current test runs successfully.

as you can see i have prepared two list of employees i.e employees and employeeList

i did so because findById method returns Optional. how can i use the streams api so that i can get list of employees simply as

List<Employee> employeeList= ....
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
sagar limbu
  • 1,202
  • 6
  • 27
  • 52
  • There is no reason to split both streams. Just use one, request the DB, filter if present, apply other filters, then collect to list. – Tom Oct 30 '18 at 10:15
  • Possible duplicate of [Using Java 8's Optional with Stream::flatMap](https://stackoverflow.com/questions/22725537/using-java-8s-optional-with-streamflatmap) – walen Oct 30 '18 at 10:49
  • @LuisG. Not a duplicate as the question is not asking about that. – Ousmane D. Oct 30 '18 at 10:53

2 Answers2

6

Just merge the two stream pipelines i.e.

List<Employee> employees = Stream.of(empIds)
                                 .map(employeeRepository::findById)
                                 .filter(Optional::isPresent)
                                 .map(Optional::get)
                               //.filter(e->e !=null) not needed as it's redundant
                                 .filter(e->e.getSalary()>200000)
                                 .collect(Collectors.toList());
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • 1
    does it work ? because i get non-static method cannot be referenced from static context on .filter(Optional::isPresent) – sagar limbu Oct 30 '18 at 10:27
  • @sagarlimbu on my phone atm but I don’t see a reason why it shouldn’t compile. Probably a type inference issue... what IDE are you using? – Ousmane D. Oct 30 '18 at 10:32
  • sorry i didn't see your answer properly. i actually used filter(Optional::get) instead of .map(Optional::get). – sagar limbu Nov 04 '18 at 07:36
4

We can write the code shorter using Optional.stream (requires Java 9+):

List<Employee> employees = Stream.of(empIds)
        .flatMap(id -> employeeRepository.findById(id).stream())
        .filter(e -> e.getSalary() > 200000)
        .collect(Collectors.toList());

According to the JavaDoc Optional.stream

returns a sequential Stream containing only that value, otherwise returns an empty Stream.

Thus we can go on with the filter operation at once.

LuCio
  • 5,055
  • 2
  • 18
  • 34
  • @Holger Added this to my answer. Thx. – LuCio Oct 30 '18 at 11:31
  • Just read your [comment](https://stackoverflow.com/questions/53048056/determine-if-a-list-composed-of-anagram-elements-in-java-8/53062631#comment93022997_53048444) about "method reference overuse" :). As I wrote how to get the code shorter, it's only consistent to follow your suggestion. Updated my answer. – LuCio Oct 30 '18 at 11:36
  • Well, I wouldn’t consider two method references “overuse”. And it’s easier to overlook when `map` and `flatMap` steps can be merged. It’s a different beast with four subsequent `map` steps… – Holger Oct 30 '18 at 11:39
  • Wouldn't you still need a check for null, since op checks for null? – matt Oct 30 '18 at 12:53
  • @matt Do you refer to `filter(e->e !=null)` in OP's code? The filtering in my answer's code takes place afer `Optional.stream()`has been flat mapped. Thus the case `e == null` cannot occour. – LuCio Oct 30 '18 at 12:59
  • So OP doesn't need it either then, since they call it after `.map(Optional::get)` – matt Oct 30 '18 at 13:02
  • 3
    Correct. The [JavaDoc](https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html#isPresent()) for `Optional.isPresent` says: "If a value is present, returns true, otherwise false." So if the `Optional` is empty, the filter will not be passed. But if it's passed, then it's safe to call `Optional.get` and the [value returned is not null](https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html#get()). – LuCio Oct 30 '18 at 13:05