I have N lists that return data from a Repository. I want to return the first non empty of these three lists (each one executes a different SQL to fetch data).
The catch is that I want to do this lazily, so that I don't need to execute a SQL on the database if I have already found an acceptable result. My code is (modified)
@Override
public List<Something> dataService(Data data) {
return firstNonEmptyList(repository.getDataWayOne(data.getParameter()),
repository.getDataWayTwo(data.getParameter()),
repository.getDataWayThree(data.getParameter().getAcessoryParameter())
Collections.singletonList(repository.getDefaultData(data.getParameter()));
}
@SafeVarargs
private final List<Something> firstNonEmptyList(List<Something>... lists) {
for (List<Something> list : lists) {
if (!list.isEmpty()) {
return list;
}
}
return null;
}
This works, but it isn't lazy. Any ideas?
You can make a stream of suppliers and evaluate them in encounter order until you find a result:
return Stream.<Supplier<List<Something>>>of(
() -> repository.getDataWayOne(data.getParameter()),
() -> repository.getDataWayTwo(data.getParameter()),
() -> repository.getDataWayThree(data.getParameter().getAcessoryParameter()),
() -> Collections.singletonList(repository.getDefaultData(data.getParameter()))
)
.map(Supplier::get)
.filter(l -> !l.isEmpty())
.findFirst()
.orElse(null);
Each supplier defines how to access a result set, without actually attempting it until map()
is executed. Since filter()
and map()
are stateless operations, each supplier will be called and its result validated before the next one is attempted. If a non-empty result is found, the stream will terminate immediately, because findFirst()
is short-circuiting.
If streams aren't your cup of tea, you can still use lambdas to achieve what you want with only some slight modifications to your original code.
public List<Something> dataService(Data data) {
return firstNonEmptyList(
() -> repository.getDataWayOne(data.getParameter()),
() -> repository.getDataWayTwo(data.getParameter()),
() -> repository.getDataWayThree(data.getParameter().getAcessoryParameter()),
() -> Collections.singletonList(repository.getDefaultData(data.getParameter()))
);
}
private final List<Something> firstNonEmptyList(Supplier<List<Something>>... listSuppliers) {
for (Supplier<List<Something>> supplier : listSuppliers) {
List<Something> list = supplier.get();
if (!list.isEmpty()) {
return list;
}
}
return null;
}
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