我有一些复杂的物体,例如猫,它具有许多属性,例如年龄,喜欢的猫食等等。
一堆Cats存储在Java Collection中,我需要找到所有3岁的猫,或者那些喜欢的猫粮是Whiskas的猫。当然,我可以编写一个自定义方法来查找具有特定属性的Cats,但这对于许多属性来说很麻烦;是否有一些通用的方法来做到这一点?
答案 0 :(得分:62)
尝试使用commons集合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
接口的实现。
答案 1 :(得分:55)
我一直在使用Google Collections(现在称为Guava)来解决此类问题。有一个名为Iterables的类可以将名为Predicate的接口作为一个非常有用的方法的参数。
Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
public boolean apply(Cat arg) { return arg.age() == 3; }
});
检查here!
答案 2 :(得分:52)
使用Java 8 lambda表达式,您可以执行类似
的操作cats.stream()
.filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
.collect(Collectors.toList());
概念上与Guava谓词方法相同,但使用lambda
看起来更清晰可能不是OP的有效答案,但值得注意的是有相似需求的人。 :)
答案 3 :(得分:45)
您可以编写一个方法,该方法接受一个定义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;
}
当然,和其他人一样,这就是为...制作关系数据库的。
答案 4 :(得分:11)
我建议使用Jxpath,它允许您对对象图进行查询,就好像它在xpath之类的
JXPathContext.newContext(cats).
getValue("//*[@drinks='milk']")
答案 5 :(得分:5)
再次使用commons集合API: 当您单独实现谓词时,您将获得“检查器”类型代码: -
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))
答案 6 :(得分:4)
您可以使用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来进行一些函数式编程很有趣。看看源代码也是如此,它非常科幻。
答案 7 :(得分:3)
仅供参考,对于使用番石榴的这个问题,还有其他3个答案,但没有人回答这个问题。提问者说他希望找到所有具有匹配属性的猫,例如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;
}
});
答案 8 :(得分:3)
使用Commons Collections:
EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);
答案 9 :(得分:1)
基于谓词,过滤器和自定义迭代器/比较器的解决方案很不错,但它们不提供模拟数据库索引。例如:我想以不同的方式搜索猫的集合:按性别,年龄和年龄,所以它看起来像两个索引: 1)[性别,年龄] 2)[年龄]。 可以对由索引访问的值进行散列,以实现快速查找,而无需迭代整个集合。有没有这样的解决方案?
答案 10 :(得分:1)
使用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;
}
});
我认为要使用这种方法。
答案 11 :(得分:1)
您可以尝试Apache Commons项目中的一些通用代码。 Collections子项目提供了用于查找与特定谓词匹配的对象的代码,以及大量谓词(equals,null,instanceof等)。 BeanUtils子项目允许您创建测试bean属性的谓词。
使用CollectionUtils类在集合中进行搜索。有几种方法,但特别是检查select()方法。 使用以下类构建谓词,或编写自己的谓词:Predicate,PredicateUtils,BeanPredicate。
这有时候有点麻烦,但至少它是通用的! : - )
答案 12 :(得分:1)
您可以使用类似JoSQL的内容,并针对您的集合编写“SQL”: http://josql.sourceforge.net/
这听起来像你想要的,还有额外的好处,可以做更复杂的查询。
答案 13 :(得分:1)
听起来很像你在.NET中使用LINQ的东西
虽然还没有针对java的“真正的”LINQ实现,但您可能希望查看Quaere,它可以轻松地完成您所描述的内容。
答案 14 :(得分:0)
JFilter http://code.google.com/p/jfilter/满足您的要求。
JFilter是一个简单而高性能的开源库,用于查询Java bean的集合。
主要功能
答案 15 :(得分:0)
当涉及到这类问题时,Guava具有非常强大的搜索功能。例如,如果您的区域基于其中一个属性搜索对象,则可以考虑:
Iterables.tryFind(listOfCats, new Predicate<Cat>(){
@Override
boolean apply(@Nullable Cat input) {
return "tom".equalsIgnoreCase(input.name());
}
}).or(new Cat("Tom"));
如果Tom cat可能不在listOfCats中,它将被返回,从而允许你避免NPE。
答案 16 :(得分:0)
一个非常常见的问题,我使用了谷歌收藏,这是我的代码
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;
}
}
希望这有帮助
答案 17 :(得分:0)
这是一个关于搜索器类的想法,您可以使用您想要查找的特定值进行参数化。
您可以进一步存储属性的名称,可能在具有所需值的Map中。在这种情况下,您将使用Cat类上的反射来调用给定属性名称的相应方法。
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.
}
}
答案 18 :(得分:0)
您可以从列表中搜索项目,如下所示。 祝你好运!
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;
}
答案 19 :(得分:0)
您可以将这些对象存储在数据库中。如果您不想要完整的数据库服务器的开销,您可以使用像HSQLDB这样的嵌入式服务器。然后,您可以使用Hibernate或BeanKeeper(更易于使用)或其他ORM将对象映射到表。您继续使用OO模型并从数据库中获得高级存储和查询优势。