Finding all objects that have a given property inside a collection
JavaCollectionsJava Problem Overview
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?
Java Solutions
Solution 1 - Java
Try the commons collections API:
List<Cat> bigList = ....; // master list
Collection<Cat> smallList = CollectionUtils.select(bigList, new Predicate() {
public boolean evaluate(Object o) {
Cat c = (Cat)o;
return c.getFavoriteFood().equals("Wiskas")
&& c.getWhateverElse().equals(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.
Solution 2 - Java
With Java 8 lambda expression you can do something like
cats.stream()
.filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
.collect(Collectors.toList());
Conceptually the same as the Guava Predicate approach, but it looks much cleaner with lambda
Probably not a valid answer for OP but worth to note for people with similar need. :)
Solution 3 - Java
I have been using Google Collections (now called Guava) 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>() {
public boolean apply(Cat arg) { return arg.age() == 3; }
});
Check it here!
Solution 4 - Java
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...
Solution 5 - Java
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']")
Solution 6 - Java
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))
Solution 7 - Java
You can use lambdaj. Things like these are trivial, the syntax is really smooth:
Person me = new Person("Mario", "Fusco", 35);
Person luca = new Person("Luca", "Marrocco", 29);
Person biagio = new Person("Biagio", "Beatrice", 39);
Person celestino = new Person("Celestino", "Bellone", 29);
List<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
List<Person> oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);
and you can do much more complicated things. It uses hamcrest for the Matchers. Some will argue that this is not Java-style, but it's fun how this guy twisted Java to make a bit of functional programming. Have a look at the source code also, it's quite sci-fi.
Solution 8 - Java
Using Commons Collections:
EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);
Solution 9 - Java
Just FYI there are 3 other answers given to this question that use Guava, but none answer the question. The asker has said he wishes to find all Cats with a matching property, e.g. age of 3. Iterables.find
will only match one, if any exist. You would need to use Iterables.filter
to achieve this if using Guava, for example:
Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
@Override
public boolean apply(Cat input) {
return input.getAge() == 3;
}
});
Solution 10 - Java
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.
Solution 11 - Java
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.
Solution 12 - Java
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! :-)
Solution 13 - Java
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:
- [sex, age]
- [age]. Values, accessed by that indexes could be hashed, to implement fast lookup, without iterating the whole collection. Is anywhere there such solution?
Solution 14 - Java
Use Google Guava.
final int lastSeq = myCollections.size();
Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
@Override
public boolean apply(@Nullable Clazz input) {
return input.getSeq() == lastSeq;
}
});
I think to use this method.
Solution 15 - Java
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.
Solution 16 - Java
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.
}
}
Solution 17 - Java
JFilter http://code.google.com/p/jfilter/ suites your requirement.
JFilter is a simple and high performance open source library to query collection of Java beans.
Key features
- Support of collection (java.util.Collection, java.util.Map and Array) properties.
- Support of collection inside collection of any depth.
- Support of inner queries.
- Support of parameterized queries.
- Can filter 1 million records in few 100 ms.
- Filter ( query) is given in simple json format, it is like Mangodb queries. Following are some examples.
- { "id":{"$le":"10"}
- where object id property is less than equals to 10.
- { "id": {"$in":["0", "100"]}}
- where object id property is 0 or 100.
- {"lineItems":{"lineAmount":"1"}}
- where lineItems collection property of parameterized type
has lineAmount equals to 1.
- where lineItems collection property of parameterized type
- { "$and":[{"id": "0"}, {"billingAddress":{"city":"DEL"}}]}
- where id property is 0 and billingAddress.city property is DEL.
- {"lineItems":{"taxes":{ "key":{"code":"GST"}, "value":{"$gt": "1.01"}}}}
- where lineItems collection property of parameterized type
which has taxes map type property of parameteriszed type has code equals to GST value greater than 1.01.
- where lineItems collection property of parameterized type
- {'$or':[{'code':'10'},{'skus': {'$and':[{'price':{'$in':['20', '40']}}, {'code':'RedApple'}]}}]}
- Select all products where product code is 10 or sku price in 20 and 40 and sku code is "RedApple".
- { "id":{"$le":"10"}
Solution 18 - Java
Guava has a very powerful searching capabilities when it comes to such type of problems. For example, if your area searching an object based on one of it properties you may consider:
Iterables.tryFind(listOfCats, new Predicate<Cat>(){
@Override
boolean apply(@Nullable Cat input) {
return "tom".equalsIgnoreCase(input.name());
}
}).or(new Cat("Tom"));
in case it's possible that the Tom cat is not in the listOfCats, it will be returned, thus allowing you to avoid NPE.
Solution 19 - Java
A very common problem and I have used google collections and here is my code
public class FindByIdPredicate implements Predicate<IDObject> {
private Long entityId;
public FindByIdPredicate(final Long entityId) {
this.entityId = entityId;
}
@Override
public boolean apply(final IDObject input) {
return input.getId().equals(this.entityId);
}
/**
* Pass in the Collection
* @param Collection
* @return IdObject if present or null
*/
public IDObject getEntity(final Collection<? extends IDObject> collection) {
for (IDObject idObject : collection) {
if (this.apply(idObject)) {
return idObject;
}
}
return null;
}
/**
* @param Set
* @return IdObject if present or null
*/
@SuppressWarnings("unchecked")
public <T> T getEntity(final Set<? extends IDObject> set) {
for (IDObject idObject : set) {
if (this.apply(idObject)) {
return (T) idObject;
}
}
return null;
}
}
Hope this helps
Solution 20 - Java
you can search item from a list
int _searchList(List<T> list,T item) {
int idx = -1;
idx = Collections.binarySearch(list,item,
new Comparator<T>() {
public int compare(Titm1,
Titm2) {
// key1
if (itm1.key1.compareTo(itm2.key1) != 0) {
return itm1.key2.compareTo(itm2.key2);
}
// key2
return itm1.key2
.compareTo(itm2.key2);
}
});
return idx;
}