I have a piece of code where am doing something like:
Optional<College> college = Optional.ofNullable(student)
.map(stud -> stud.getCollege())
.get()
.stream()
.filter(college -> Objects.nonNull(college.getCollegeName()))
.findFirst();
Now, while writing an unit test, I got a catch that what if student comes as null?
It would be effectively like:
Optional.empty() // the same as the student is null
.map(stud -> stud.getCollege())
.get()
.stream()
.filter(college -> Objects.nonNull(college.getCollegeName()))
.findFirst();
Which I think is not fine because I am getting Exception
expected<com.src.exceptions.CollegeNotFoundException> but
was<java.util.NoSuchElementException>
#Update
Updating the question details for clarifications
Yes stud.getCollege() returns a list<>
I agree with #Nikolas approach except that you should not return null, returning null at last is against using Optional
What about this one:
Optional<College> optional = Optional.ofNullable(student)
.map(stud -> stud.getCollegeList())
.orElse(Collections.emptyList())
.stream()
.filter(c -> Objects.nonNull(c.getCollegeName()))
.findFirst();
Calling Optional::get with no previous check Optional::isPresent is dangerous because it might produce CollegeNotFoundException. And it is not the way the Optional shall be used. The idea of Optional is mapping/filtering the values and providing a default value if the Optional ends up with no element (empty).
Assuming Student::getCollege returns List<College> having method College::getCollegeName, you can do the following:
College college = Optional.ofNullable(student)
.map(stud -> stud.getCollege())
// if Optional is empty, then use an empty collection
.orElse(Collections.emptyList())
.stream()
.filter(c -> Objects.nonNull(c.getCollegeName()))
.findFirst()
// get the value or else college is null
.orElse(null);
As long as stud.getCollege() returns null, the Optional becomes empty and an empty list will be streamed. And again the same principle is applied: As long as the list is empty, the filter and findFirst are not be called and null is safely returned (or any default value you wish).
Also note that the line .filter(c -> Objects.nonNull(c.getCollegeName())) might also produce NullPointerException as long as there is not guaranteed stud.getCollege() doesn't return a list with a null element (remember the list is not null itself so Optional treats it as a "valuable" item). The safe code actually looks like:
Optional<College> college = Optional.ofNullable(student)
.map(stud -> stud.getCollege())
.orElse(Collections.emptyList())
.stream()
.filter(c -> c != null && c.getCollegeName() != null)
.findFirst();
Actually, I prefer to return either a null-object, null or Optional itself.
Related
I have a list of objects say car. I want to filter this list based on some parameter using Java 8. But if the parameter is null, it throws NullPointerException. How to filter out null values?
Current code is as follows
requiredCars = cars.stream().filter(c -> c.getName().startsWith("M"));
This throws NullPointerException if getName() returns null.
In this particular example, I think #Tagir is 100% correct get it into one filter and do the two checks. I wouldn't use Optional.ofNullable the Optional stuff is really for return types not to be doing logic... but really neither here nor there.
I wanted to point out that java.util.Objects has a nice method for this in a broad case, so you can do this:
cars.stream()
.filter(Objects::nonNull)
Which will clear out your null objects. For anyone not familiar, that's the short-hand for the following:
cars.stream()
.filter(car -> Objects.nonNull(car))
To partially answer the question at hand to return the list of car names that starts with "M":
cars.stream()
.filter(car -> Objects.nonNull(car))
.map(car -> car.getName())
.filter(carName -> Objects.nonNull(carName))
.filter(carName -> carName.startsWith("M"))
.collect(Collectors.toList());
Once you get used to the shorthand lambdas you could also do this:
cars.stream()
.filter(Objects::nonNull)
.map(Car::getName) // Assume the class name for car is Car
.filter(Objects::nonNull)
.filter(carName -> carName.startsWith("M"))
.collect(Collectors.toList());
Unfortunately once you .map(Car::getName) you'll only be returning the list of names, not the cars. So less beautiful but fully answers the question:
cars.stream()
.filter(car -> Objects.nonNull(car))
.filter(car -> Objects.nonNull(car.getName()))
.filter(car -> car.getName().startsWith("M"))
.collect(Collectors.toList());
You just need to filter the cars that have a null name:
requiredCars = cars.stream()
.filter(c -> c.getName() != null)
.filter(c -> c.getName().startsWith("M"));
The proposed answers are great. Just would like to suggest an improvement to handle the case of null list using Optional.ofNullable, new feature in Java 8:
List<String> carsFiltered = Optional.ofNullable(cars)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
So, the full answer will be:
List<String> carsFiltered = Optional.ofNullable(cars)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull) //filtering car object that are null
.map(Car::getName) //now it's a stream of Strings
.filter(Objects::nonNull) //filtering null in Strings
.filter(name -> name.startsWith("M"))
.collect(Collectors.toList()); //back to List of Strings
You can do this in single filter step:
requiredCars = cars.stream().filter(c -> c.getName() != null && c.getName().startsWith("M"));
If you don't want to call getName() several times (for example, it's expensive call), you can do this:
requiredCars = cars.stream().filter(c -> {
String name = c.getName();
return name != null && name.startsWith("M");
});
Or in more sophisticated way:
requiredCars = cars.stream().filter(c ->
Optional.ofNullable(c.getName()).filter(name -> name.startsWith("M")).isPresent());
Leveraging the power of java.util.Optional#map():
List<Car> requiredCars = cars.stream()
.filter (car ->
Optional.ofNullable(car)
.map(Car::getName)
.map(name -> name.startsWith("M"))
.orElse(false) // what to do if either car or getName() yields null? false will filter out the element
)
.collect(Collectors.toList())
;
you can use this
List<Car> requiredCars = cars.stream()
.filter (t-> t!= null && StringUtils.startsWith(t.getName(),"M"))
.collect(Collectors.toList());
In my method below, the variable n can be null, and if it is null I want to return an empty list eg something like List.of. How can I amend the below method to ensure that if a null is found, an empty List is returned and not removed? I tried using filter which I've commented out below, but that just removes any instances where null is found.
private List<Account> getNames(MetaData metadata){
return metadata.getNames()
.stream()
.map(n -> createAccount(n, metadata.getType()))
//.filter(n -> n.getFinalName() != null)
.collect(Collectors.toList());
}
private Account createAccount(String n, Metadata metadata){...}
You can use ternary operator
return metadata.getNames()
.stream()
.map(n -> n!=null ? createAccount(n, metadata.getType()) : /* else do something */)
.collect(Collectors.toList());
I have code like this:
myList.stream()
.filter(item -> item.getName().isPresent())
.filter(item -> item.getName().get().equalsIgnoreCase(otherName))
.findFirst();
(... where item.getName() has a return type of Optional<String>)
How can I condense two filters into one here?
You can use Optional.filter():
myList.stream()
.filter(item ->
item.getName().filter(n -> n.equalsIgnoreCase(otherName)).isPresent())
.findFirst();
The inner filter call is Optional.filter, which returns an empty optional if the filter condition was not met.
You can avoid it with Optional::stream() method:
If a value is present, returns a sequential Stream containing only that value, otherwise returns an empty Stream.
myList.stream()
.map(Item::getName)
.flatMap(Optional::stream)
.filter(otherName::equalsIgnoreCase)
.findFirst();
I would solve it the "OOP" way. Make a method in Item 'isNameEqual' or something like that which would hide the logic with the optional and the ignore case.
Then you can have just 1 filter statement with Item::isNameEqual.
You don't want to have too much ceremony in the lambda.
Combine the filters using a logical AND (&&):
myList.stream()
.filter(item -> item.getName().isPresent()
&& item.getName().get().equalsIgnoreCase(otherName))
.findFirst();
This is safe because if the value is not present, the && will short-circuit to false.
Or use the conditional operator:
myList.stream()
.filter(item -> item.getName().isPresent()
? item.getName().get().equalsIgnoreCase(otherName)
: false)
.findFirst();
Another way would be to use null as the value if the Optional<String> has no value and equalsIgnoreCase(null) will always return false:
myList.stream()
.filter(item -> otherName.equalsIgnoreCase(item
.getName()
.orElseGet(null)))
.findFirst();
I am getting list of objects from API
public Optional<List<Employee>> getEmployeeData (String deptId){
List<Employee> employee = departmentClient.employeeData(deptId);
//Based on some condition I am filtering employee list but before that I want to check for null for list.
return Optional.ofNullable(employee).orElse(Collections.emptyList())
.stream()
.filter(Objects::nonNull)
.filter(e -> e.getType != null)
.collect(Collectors.toList());
}
But I think as method return type is Optional<> this is giving error. How can I check null for the List before the Stream and return as an Optional<List<..>>
You returned List<Employee> while your method signature is Optional<List<Employee>>
Try this one:
return employee != null ? Optional.of(employee.stream()
.filter(Objects::nonNull)
.filter(e -> e.getType != null)
.collect(Collectors.toList())) : Optional.ofNullable(Collections.emptyList());
Your solution doesn't work because the result of Optional is List and you collect it through the Stream pipelines back to the List.
Using Java 8 you can wrap all your solution inside Optional or better use the advantage of the Collectors instead:
Optional<List<Employee>> o = Optional
.ofNullable(employees) // employees can be null, right?
.orElse(Collections.emptyList()) // ... if so, then empty List
.stream() // Stream<Employee>
.filter(Objects::nonNull) // Stream<Employee> filtered as non-nulls
.filter(e -> e.getType() != null) // Stream<Employee> with non-null field
.collect(Collectors.collectingAndThen(
Collectors.toList(), // Collected to List<Employee>
Optional::of)); // Collected to Optional<List<Employee>>
The Collectors::collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) method behaves as usual Collector providing a subsequent mapping function taking the collected result. In our case, we simply wrap the List into Optional to be returned.
Collector downstream collects to List<Employee>
Function finisher maps List<Employee> to Optional<List<Employee>>
With Java 9 and higher using Optional::stream, the beginning might be little different:
Optional<List<Employee>> o = Optional
.ofNullable(employees) // null-safe employees
.stream() // Stream<List<Employees>>
.flatMap(List::stream) // Stream<Employees>
.filter(Objects::nonNull)
......
And yet another option:
return Optional.ofNullable(employee)
.map(list -> list.stream()
.filter(Objects::nonNull)
.filter(e -> e.getType() != null)
.collect(Collectors.toList()));
The lambda inside .map(...) is executed only if the emploee list is not null, otherwise an empty Optional is returned.
I am trying to apply stream on a list which can be null as it is calling a repository method and I want to get the first element from it if it is not null and compare one of its parameter with request value.
Optional.ofNullable(placementRepository.findAllByAccountId(accountId))
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.findFirst()
.get()
.getPlacementDate()
.isAfter(placementRequest.getPlacementDate())
Currently it is failing at .get if the list itself is null though I filtered with nonNull. I want stream the list if the list is not null and get the first element and compare its parameter against other value. I tried with other alternative filters but no luck
I want stream the list if the list is not null and get the first
element and compare its parameter against other value.
You didn't think of the case of empty or null elements in the returned List of the repository. You have to choose a default value or execute the date comparison only if the optional is present.
For example with a default date value :
final LocalDate defaultDateValue = ...;
Optional.ofNullable(placementRepository.findAllByAccountId(accountId))
.flatMapping(l -> l.stream()
.filter(Objects::nonNull)
.findFirst()
.mapping(o -> o.getPlacementDate())
)
.orElse(defaultDateValue)
.isAfter(placementRequest.getPlacementDate())
With a conditional processing :
Optional<LocalDate> opt =
Optional.ofNullable(placementRepository.findAllByAccountId(accountId))
.flatMapping(l -> l.stream()
.filter(Objects::nonNull)
.findFirst()
.mapping(o -> o.getPlacementDate())
)
if (opt.isPresent()){
boolean isAfter = opt.get().isAfter(placementRequest.getPlacementDate());
// ...
}
Optional<T> findFirst();, so in case you have null or empty stream, then this method returns null.
You should use .findFirst().orElseGet(() -> /* NULL object*/)
The thing is that .findFirst() is able to return no result at all and .get() will fail. You can use .ifPresent() or use .orElseGet()
Considering the type of elements in your list is YourPojo, this code snippet should do what you need:
List<YourPojo> list = placementRepository.findAllByAccountId(accountId);
LocalDate date = placementRequest.getPlacementDate();
boolean flag =
Optional.ofNullable(list) // Optional<List<YourPojo>>
.flatMap(Collection::stream) // Stream<YourPojo>
.filter(Objects::nonNull) // Stream<YourPojo>
.findFirst() // Optional<YourPojo>
.map(YourPojo::getPlacementDate) // Optional<LocalDate>
.map(d -> d.isAfter(date)) // Optional<Boolean>
.orElse(false); // Boolean
Note: If you're using Java 11 or above then .map(d -> d.isAfter(date)) can be replaced by .map(not(date::isBefore)). Not sure if it's more readable though.