我有一个复杂的对象,比如猫,它有许多属性,比如年龄、最喜欢的猫粮等。
一堆猫被存储在Java集合中,我需要找到所有年龄为3岁的猫,或者那些最喜欢的猫粮是Whiskas的猫。当然,我可以编写自定义方法来查找具有特定属性的猫,但是如果有许多属性,这将变得很麻烦;是否有一种通用的方法可以做到这一点?
我有一个复杂的对象,比如猫,它有许多属性,比如年龄、最喜欢的猫粮等。
一堆猫被存储在Java集合中,我需要找到所有年龄为3岁的猫,或者那些最喜欢的猫粮是Whiskas的猫。当然,我可以编写自定义方法来查找具有特定属性的猫,但是如果有许多属性,这将变得很麻烦;是否有一种通用的方法可以做到这一点?
试试使用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);
}
});
当然你不必每次都使用匿名类,你可以为常用的搜索创建Predicate
接口的实现。
使用Java 8的Lambda表达式,您可以像这样做:
cats.stream()
.filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
.collect(Collectors.toList());
与Guava Predicate方法在概念上相同,但使用lambda更加清晰。
对于楼主可能不是一个有效的答案,但对于有类似需求的人来说值得一提。:)
check(Cat)
方法,在该方法中,可以使用任何属性检查来实现该方法。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;
}
我建议使用Jxpath,它允许你像使用XPath一样在对象图上执行查询。
JXPathContext.newContext(cats).
getValue("//*[@drinks='milk']")
再次提到Commons Collections API: 当你单独实现Predicate时,你会得到“checker”类型的代码:
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;
}
}
这被用作:
CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))
你可以使用lambdaj。像这样的东西是微不足道的,语法非常流畅:
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);
你可以做更多复杂的事情。它使用hamcrest进行匹配。有些人会认为这不是Java风格,但这个家伙很有趣地扭曲了Java,使它具有一些函数式编程的特点。同时也可以看一下源代码,非常科幻。
仅供参考,此问题已有其他3个答案使用了Guava,但是没有回答问题。提问者说他希望找到所有属性匹配的猫,例如年龄为3岁。Iterables.find
只会匹配一个,如果存在的话。如果你要使用Guava实现这一点,你需要使用Iterables.filter
,例如:
Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
@Override
public boolean apply(Cat input) {
return input.getAge() == 3;
}
});
使用Commons Collections:
EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);
final int lastSeq = myCollections.size();
Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
@Override
public boolean apply(@Nullable Clazz input) {
return input.getSeq() == lastSeq;
}
});
我认为使用这种方法。