Reputation: 1074
I'm looking for a Google Collections method that returns the first result of a sequence of Suppliers that doesn't return null.
I was looking at using Iterables.find() but in my Predicate I would have to call my supplier to compare the result against null, and then have to call it again once the find method returned the supplier.
Upvotes: 1
Views: 1539
Reputation: 40851
You asked for how to do this using Google Collections, but here's how you would do it without using Google Collections. Compare it to Cowan's answer (which is a good answer) -- which is easier to understand?
private static Thing findThing(List<Supplier<Thing>> thingSuppliers) {
for (Supplier<Thing> supplier : thingSuppliers) {
Thing thing = supplier.get();
if (thing != null) {
return thing;
}
}
// throw exception or return null
}
In place of the comment -- if this was the fault of the caller of your class, throw IllegalArgumentException or IllegalStateException as appropriate; if this shouldn't have ever happened, use AssertionError; if it's a normal occurrence your code that invokes this expects to have to check for, you might return null.
Upvotes: 3
Reputation: 17601
What is wrong with this?
List<Supplier> supplierList = //somehow get the list
Supplier s = Iterables.find(supplierList, new Predicate<Supplier>(){
boolean apply(Supplier supplier) {
return supplier.isSomeMethodCall() == null;
}
boolean equals(Object o) {
return false;
}
});
Are you trying to save some lines? The only optimisation I can think is to static import the find so you can get rid of "Iterables". Also the predicate is an anonymous inner class, if you need it in more than one place you can create a class and it would look as,
List<Supplier> supplierList = //somehow get the list
Supplier s = find(supplierList, new SupplierPredicateFinder());
Where SupplierPredicateFinder is another class.
UPDATE : In that case find is the wrong method. You actually need a custom function like this which can return two values. If you are using commons-collections then you can use a DefaultMapEntry or you can simply return an Object[2] or a Map.Entry.
public static DefaultMapEntry getSupplier(List<Supplier> list) {
for(Supplier s : list) {
Object heavyObject = s.invokeCostlyMethod();
if(heavyObject != null) {
return new DefaultMapEntry(s, heavyObject);
}
}
}
Replace the DefaultMapEntry with a List of size 2 or a hashmap of size 1 or an array of length 2 :)
Upvotes: -2
Reputation: 37543
Given your comment to Calm Storm's answer (the desire not to call Supplier.get()
twice), then what about:
private static final Function<Supplier<X>, X> SUPPLY = new Function<....>() {
public X apply(Supplier<X> in) {
// If you will never have a null Supplier, you can skip the test;
// otherwise, null Supplier will be treated same as one that returns null
// from get(), i.e. skipped
return (in == null) ? null : in.get();
}
}
then
Iterable<Supplier<X>> suppliers = ... wherever this comes from ...
Iterable<X> supplied = Iterables.transform(suppliers, SUPPLY);
X first = Iterables.find(supplied, Predicates.notNull());
note that the Iterable that comes out of Iterables.transform()
is lazily-evaluated, therefore as Iterables.find()
loops over it, you only evaluate as far as the first non-null
-returning one, and that only once.
Upvotes: 5