Issue
I am learning Java 11, and wanted to write the below logical block using Java 11. Any inputs would be helpful. As it has many conditional checks can we use lambda or streams to achieve the below logical block.
@Data
class MyDetails{
private String name;
private String status;
private Integer spreadValue;
private Object value;
...
}
public List<MyDetails> returnValidList(Collection<MyDetails> myDetails){
for (MyDetails myDetail : myDetails){
if (myDetail !=null && myDetail.getValue()!=null){
if (myDetail.getSpreadValue()==3 || myDetail.getSpreadValue()==4){
if (myDetail.getName().equalsIgnoreCase("AUTO") || myDetail.getName().equalsIgnoreCase("HOME")){
result.add(myDetail);
} else {
result.add(myDetail);
}
}
}
return result;
}
}
I tried the below code, but the list size is zero, expected list size is 17. Any suggestion to correct the below snippet.
List<MyDetails> result = myDetails.stream()
.filter(Objects::nonNull)
.filter(obj -> Objects.nonNull(obj.getValue()))
.filter(obj -> obj.getSpreadValue() == 3 || obj.getSpreadValue() == 4)
.collect(Collectors.toList());
Solution
The only thing you can do is to chain the filter
calls through the Stream API.
List<MyDetails> result = myDetails.stream()
.filter(Objects::nonNull)
.filter(obj -> Objects.nonNull(obj.getValue()))
.filter(obj -> obj.getSpreadValue() == 3 || obj.getSpreadValue() == 4)
.filter(obj -> obj.getName().equalsIgnoreCase("AUTO") || obj.getName().equalsIgnoreCase("HOME"))
.collect(Collectors.toList());
In more complex way, you might want to extract the condition(s) to a separate variables or methods (returning Predicate<MyDetails>
. However, in case of advanced and configurable filtering, I recommend to iterate a collection of such predicated that can be filtered first before the application:
// Predicate list
List<Predicate<MyDetails>> predicates = List.of(
Objects::nonNull, // should be always first
obj-> Objects.nonNull(obj.getValue()),
obj-> obj.getSpreadValue() == 3 || obj.getSpreadValue() == 4,
obj-> obj.getName().equalsIgnoreCase("AUTO") || obj.getName().equalsIgnoreCase("HOME")
);
// Reduction using AND. If no predicate is qualified,
// ... a predicate that everything passes through is returned (identity)
Predicate<MyDetails> predicate = predicates.stream()
.reduce(obj -> true, Predicate::and);
// Apply the predicate
List<MyDetails> result = myDetails.stream()
.filter(predicate)
.collect(Collectors.toList());
Notes about the implementation:
- Stream API in this case is no improvement, except you want a configurable or advanced filtering.
- If you want to have configurable predicated, use
LinkedHashMap
and filter by keys the unwanted predicates.
And some code review as long as your code seems very imcomplete (ahtough I did my best to format it and make it clear):
- You miss another
return
in your method. private Something value;
is missing in theMyDetails
definition.- The
List<MyDetails>
is undefined in the method (remember you callgetValue()
). obj.getSpreadValue() == 3 || obj.getSpreadValue() == 4)
andobj.getName().equalsIgnoreCase("AUTO") || obj.getName().equalsIgnoreCase("HOME")
are subjects of refactoring. I'd recommend to useHashSet
andSet::contains
.- The condition checking for
AUTO
andHOME
names is irrelevant since both branches results in the addition of the item to the list.
Answered By - Nikolas Charalambidis
Answer Checked By - Senaida (JavaFixing Volunteer)