views:

3379

answers:

13

This problem occurs over and over. I have some complicated object, such as a Cat, which has many properties, such as age, favorite cat food, and so forth.

A bunch of Cats are stored in a Java Collection, and I need to find all the Cats that are aged 3, or those whose favorite cat food is Whiskas. Surely, I can write a custom method that finds those Cats with a specific property, but this gets cumbersome with many properties; is there some generic way of doing this?

+10  A: 

You could write a method that takes an instance of an interface which defines a check(Cat) method, where that method can be implemented with whatever property-checking you want.

Better yet, make it generic:

public interface Checker<T> {
    public boolean check(T obj);
}

public class CatChecker implements Checker<Cat> {
    public boolean check(Cat cat) {
        return (cat.age == 3); // or whatever, implement your comparison here
    }
}

// put this in some class
public static <T> Collection<T> findAll(Collection<T> coll, Checker<T> chk) {
    LinkedList<T> l = new LinkedList<T>();
    for (T obj : coll) {
         if (chk.check(obj))
             l.add(obj);
    }
    return l;
}

Of course, like other people are saying, this is what relational databases were made for...

David Zaslavsky
I'd say the same, but suggest using Comparator instead of a custom interface.
Paul Tomblin
The reason I didn't use Comparator is that a Comparator is supposed to impose a total ordering on the collection, whereas all we need is a boolean test of some condition. The property/ies being checked might not have any meaningful order.
David Zaslavsky
This is the way to do it. If you only need a particular Checker once, just write it as an anonymous inner class. For something like checking for cats of age 3, i'd actually write a method that takes the age, and returns a new Checker that finds cats of that age.
Adam Jaskiewicz
What's up with this? Has nobody heard of Commons Collections, Google Collections or Hamcrest collections? I can't believe this has so many upvotes or that it's accepted.
Stephen
(1) it's nice to know how it's implemented (2) it's usually not worth bringing in a whole extra library just for a simple method like this.
David Zaslavsky
@David: +1 for argument (2). Seen this a thousand times: import extra lib, extra jar, extra repo for one single class of 20 lines. Keep a sense of proportion!
digitalarbeiter
+1  A: 

You could store those objects on a database. If you don't want the overhead of a full blown database server you can use an embedded one like HSQLDB. Then you can use Hibernate or BeanKeeper (simpler to use) or other ORM to map objects to tables. You keep using the OO model and get the advanced storage and querying benefits from a database.

Cesar
+1  A: 

Sounds a lot like something you would use LINQ for in .NET

While there's no "real" LINQ implementation for java yet, you might want to have a look at Quaere which could do what you describe easily enough.

Eric Petroelje
Whoa, very cool.
Outlaw Programmer
A: 

You can use something like JoSQL, and write 'SQL' against your collections: http://josql.sourceforge.net/

Which sounds like what you want, with the added benefit of being able to do more complicated queries.

duwanis
A: 

You could try some of the generic code in the Apache Commons project. The Collections subproject provides code for finding objects that match a particular Predicate, as well as a large number of predicates (equals, null, instanceof, etc). The BeanUtils subproject allows you to make predicates that test properties of beans.

Use the CollectionUtils class to search within a collection. There are a few methods for this, but check out the select() method, in particular. Use the following classes to construct predicates, or write your own: Predicate, PredicateUtils, BeanPredicate.

This is all sometimes a bit cumbersome, but at least it's generic! :-)

Rich Dougherty
This is pretty neat. It isn't as compact as jxpath which is what I would have answered, but I like that you can build things programatically .
Dave Stenglein
A: 

Here's an idea for a searcher class you could parameterize with the specific values you want to look for.

You could go further and store the names of the properties as well, probably in a Map with the desired values. In this case you would use reflection on the Cat class to call the appropriate methods given the property names.

public class CatSearcher {

  private Integer ageToFind = null;
  private String  foodToFind = null;

  public CatSearcher( Integer age, String food ) {
    this.ageToFind = age;
    this.foodToFind = food;
  }

  private boolean isMatch(Cat cat) {
    if ( this.ageToFind != null && !cat.getAge().equals(this.ageToFind) ) {
       return false;
    }
    if ( this.foodToFind != null && !cat.getFood().equals(this.foodToFind) {
       return false;
    }
    return true;
  }

  public Collection<Cat> search( Collection<Cat> listToSearch ) {
    // details left to the imagination, but basically iterate over
    // the input list, call isMatch on each element, and if true
    // add it to a local collection which is returned at the end.
  }

}
Dave Costa
+3  A: 

I suggest using Jxpath, it allows you to do queries on object graphs as if it where xpath like

JXPathContext.newContext(cats).
     getValue("//*[@drinks='milk']")
flybywire
+1 Interesting (even though XPath is not the easiest thing to learn to use effectively)
Jonik
+2  A: 

I have been using Google Collections for this kind of problem. There is a class called Iterables that can take an interface called Predicate as a parameter of a method that is really helpful.

Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
    boolean apply(Cat arg) { return arg.age() == 3; }
});

Check it here!

rcouto
+4  A: 

Try the commons collections API:

List<Cat> bigList = ....; // master list

List<Cat> smallList = CollectionUtils.find(new Predicate() {
    public boolean evaluate(Object o) {
        Cat c = (Cat)o;
        return c.getFavoriteFood()=="Wiskas" 
            && c.getWhateverElse()==Something;
    }
});

Of course you don't have to use an anonymous class every time, you could create implementations of the Predicate interface for commonly used searchs.

Brian Dilley
A: 

Again with the commons collections API: You get the "checker" type code when you Implement the Predicate Separately:-

public class CatPredicate implements Predicate {

    private int age; 


    public CatPredicate(int age) {
        super();
        this.age = age;
    }


    @Override
    public boolean evaluate(Object o) {
        Cat c (Cat)o;
        return c.getAge()==this.age;
    }

}

which gets used as :-

CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))
Coral
A: 

Solutions based on predicates, filters and custom iterators/comparators are nice, but they don't provide analogue with database index. For instance: I would like to search collection of cats different ways: by sex and age and by age, so it looks like two indexes: 1) [sex, age] 2) [age]. Values, accessed by that indexes could be hashed, to implement fast lookup, without iterating the whole collection. Is anywhere there such solution?

Shaman
A: 

Write it in scala :)

collection.filter(_.age > 3 && _.sex == 'male') //etc...

Mond Raymond