Reputation: 880
I've got the following classes:
public class Insurance {
...
}
public class Customer {
private List<Insurance> insurances;
public List<Insurance> getInsurances() {
return insurances;
}
...
}
public class CustomerRegistry {
private List<Customer> customers;
...
}
as well as this helper method, which reduces a List<Predicate<T>>
into a single Predicate<T>
:
public Predicate<T> reducePredicates(List<Predicate<T>> predicates) {
return predicates.stream()
.reduce(Predicate::and)
.orElse(p -> true);
}
What I want to do is get a list of insurances that match a list of filters, belonging to customers that match a list of filters. If this is unclear, the below code will hopefully clarify.
Method is inside CustomerRegistry
class above.
public List<Insurance> findInsurances(List<Predicate<Customer>> cusPredicates,
List<Predicate<Insurance>> insPredicates) {
List<Insurance> matches = new LinkedList<>();
customers.stream()
.filter(reducePredicates(cusPredicates)
.forEach(cus -> cus.getInsurances()
.stream()
.filter(reducePredicates(insPredicates))
.forEach(cus -> matches.add(cus)))
return matches;
}
Is there a way to do this without the matches
list? Can I perform some sort of reduction, so that the matching insurances are returned directly (i.e. not being added to a temporary collection like matches
)?
Upvotes: 20
Views: 26635
Reputation: 692231
Use flatMap():
customers.stream()
.filter(reducePredicates(cusPredicates))
.flatMap(cus -> cus.getInsurances().stream())
.filter(reducePredicates(insPredicates))
.collect(Collectors.toList())
Or better, to avoid reducing the predicates over and over again:
Predicate<Customer> customerPredicate = reducePredicates(cusPredicates);
Predicate<Customer> insurancePredicate = reducePredicates(insPredicates);
List<Insurance> =
customers.stream()
.filter(customerPredicate)
.flatMap(cus -> cus.getInsurances().stream())
.filter(insurancePredicate)
.collect(Collectors.toList())
Upvotes: 20