views:

301

answers:

3

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.

A: 

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 :)

Calm Storm
On the Supplier (a Google Collections) interface there is a single method called get(). In this is a time consuming computation. Iterables.find returns the Supplier when the predicate matches, not the result of get(). So after the result of find() I would need to call get again, which would need to perform the computation again. I could use a MemoizingSupplier but it would be nicer if the find method returned the result of the Supplier and not the Supplier itself.
pledge
Updated the answer with some more code samples !
Calm Storm
This answer is incoherent. The first half is invalid according to the text of the original question asked. In the second half, why are you returning both the supplier and the heavyObject when the user only wants the heavyObject? I'm bothered that we would be so quick to recommend the Map.Entry/Object[2] hack (and it is a grotesque hack) to anyone except when *really* called for.
Kevin Bourrillion
I feel I should keep explaining more, since I downvoted: as well, the question is specifically about gc's Supplier class and you seem to have never even looked at it (you'd have found that it's generic and has a method called get()). And your last method there doesn't compile (return statement expected at the end). I'm just saying all these little things add up to an answer that's not very useful.
Kevin Bourrillion
"the question is specifically about gc's Supplier class" - The asker has not explicitly stated so. I was just assuming it was his domain class."And your last method there doesn't compile" - I was trying to show a general structure not a readymade code that one can paste into an IDE"The first half is invalid according to the text of the original question asked." - The question was edited after my answer (see Cowan's answer with my reference)
Calm Storm
+5  A: 

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.

Cowan
+1 for answering the question asked well. Still, I think the question asked should have been "what is the best way to do this", not "what's a way to do this that uses google collections", so I've offered another answer.
Kevin Bourrillion
This did work for me.
pledge
You should accept this as the answer then.
harschware
Accepting this as it answered the original question, but also read Kevin's comments.
pledge
+3  A: 

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.

Kevin Bourrillion
This is the solution I originally had, I should have really posted it in the question. However, as we have been finding some nice uses for Predicates and Functions I was looking to see if something like Suppliers.firstSupplied existed. As Cowan's (working) suggestion shows this is probably a case where a functional style in Java makes for less understandable code. The tests pass with both solutions.
pledge