查找集合中具有给定属性的所有对象
我有一些复杂的东西,比如猫,它有很多属性,比如年龄,喜欢的猫食等等。
一堆猫被存储在一个Java集合中,我需要find所有三岁的猫,或者那些最喜欢的猫食是Whiskas的猫。 当然,我可以编写一个自定义的方法来find那些具有特定属性的猫,但是这会让许多属性变得繁琐; 有没有一些通用的方式来做到这一点?
你可以编写一个方法,它接受一个定义一个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; }
当然,就像其他人所说的那样,这就是关系数据库的作用。
尝试公共收集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); } });
当然,您不必每次都使用匿名类,您可以为常用search创buildPredicate
接口的实现。
我一直在使用Google Collections(现在称为Guava )来解决这类问题。 有一个名为Iterables的类,可以将一个名为Predicate的接口作为一个真正有用的方法的参数。
Cat theOne = Iterables.find(cats, new Predicate<Cat>() { public boolean apply(Cat arg) { return arg.age() == 3; } });
在这里检查!
有了Java 8 lambdaexpression式,你可以做类似的事情
cats.stream() .filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS ) .collect(Collectors.toList());
从概念上讲,与“番石榴谓词”(Guava Predicate)方法一样,但使用lambda看起来更清晰
可能不是OP的有效答案,但值得注意的是有类似需求的人。 🙂
我build议使用Jxpath ,它允许你在对象图上做查询,就好像它在xpath那样
JXPathContext.newContext(cats). getValue("//*[@drinks='milk']")
再次使用公共集合API:当您单独实现谓词时,您将获得“检查器”types的代码: –
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为Matchers 。 有人会认为这不是Java风格,但是这个家伙如何扭曲Java来做一些function性编程是很有趣的。 也看看源代码,这是相当科幻。
使用Commons Collections:
EqualPredicate nameEqlPredicate = new EqualPredicate(3); BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate); return CollectionUtils.filter(cats, beanPredicate);
只是FYI有3个其他答案给这个问题,使用番石榴,但没有回答这个问题。 提问者表示,他希望find所有具有匹配属性的猫,例如3岁Iterables.find
将只匹配一个,如果存在的话。 例如,如果使用番石榴,则需要使用Iterables.filter
来实现此目的。
Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() { @Override public boolean apply(Cat input) { return input.getAge() == 3; } });
听起来很像你将在.NET中使用LINQ的东西
尽pipejava还没有“真正的”LINQ实现,但是你可能想看一下Quaere ,它可以做你所描述的足够简单的东西。
你可以使用诸如JoSQL之类的东西,并且对你的集合写'SQL': http ://josql.sourceforge.net/
这听起来像你想要的,能够做更复杂的查询的额外好处。
您可以尝试Apache Commons项目中的一些通用代码。 Collections子项目提供用于查找匹配特定Predicate的对象的代码,以及大量的谓词(equals,null,instanceof等)。 BeanUtils子项目允许您制定testingBean属性的谓词。
使用CollectionUtils类在集合中进行search。 有几个方法,但特别是检查select()方法。 使用以下类构造谓词,或者编写自己的谓词 : PredicateUtils , BeanPredicate 。
这有时候有点麻烦,但至less是通用的! 🙂
基于谓词,filter和自定义迭代器/比较器的解决scheme很好,但是它们不提供数据库索引的模拟。 例如:我想按照不同的方式search猫的收集方式:按性别,年龄和年龄,看起来像两个指标:1)[性别,年龄] 2)[年龄]。 可以对由该索引访问的值进行散列,以实现快速查找,而无需迭代整个集合。 有没有这样的解决scheme?
使用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; } });
我想用这个方法。
您可以将这些对象存储在数据库中。 如果你不想要一个完整的数据库服务器的开销,你可以使用像HSQLDB这样的embedded式数据库服务器。 然后,您可以使用Hibernate或BeanKeeper(更简单易用)或其他ORM将对象映射到表。 您继续使用OO模型并从数据库获取高级存储和查询优势。
这里有一个search类的想法,你可以参数化你想要查找的特定值。
你可以进一步存储这些属性的名字,可能在一个具有所需值的地图中。 在这种情况下,您将使用Cat类的reflection来调用给定属性名称的适当方法。
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. } }
JFilter http://code.google.com/p/jfilter/套装您的需求。;
JFilter是一个简单而高性能的开源库,用于查询Java bean的集合。
主要特征
- 支持collection(java.util.Collection,java.util.Map和Array)属性。
- 支持任何深度收集里面的集合。
- 支持内部查询。
- 支持参数化查询。
- 可以在几百毫秒内过滤100万条logging。
- filter(查询)以简单的json格式给出,就像Mangodb查询一样。 以下是一些例子。
- {“id”:{“$ le”:“10”}
- 对象id属性小于等于10。
- {“id”:{“$ in”:[“0”,“100”]}}
- 对象id属性是0或100。
- { “了LineItem”:{ “lineAmount”: “1”}}
- 其中参数化types的lineItems集合属性的lineAmount等于1。
- {“$ and”:[{“id”:“0”},{“billingAddress”:{“city”:“DEL”}}]}
- 其中id属性为0,而billingAddress.city属性为DEL。
- {“lineItems”:{“taxes”:{“key”:{“code”:“GST”},“value”:{“$ gt”:“1.01”}}}}
- 其中具有参数化types的参数化types的lineItems集合属性的代码等于大于1.01的GST值。
- {'$ or':[{'code':'10'},{'skus':{'$ and':[{'price':{'$ in':['20','40']} },{'code':'RedApple'}]}}]}
- select产品代码为10或sku价格为20,40的所有产品,sku代码为“RedApple”。
- {“id”:{“$ le”:“10”}
当涉及到这类问题时,番石榴具有非常强大的search能力。 例如,如果您的区域根据其中一个属性search对象,则可能会考虑:
Iterables.tryFind(listOfCats, new Predicate<Cat>(){ @Override boolean apply(@Nullable Cat input) { return "tom".equalsIgnoreCase(input.name()); } }).or(new Cat("Tom"));
如果可能Tom猫不在listOfCats中,它将被返回,从而允许你避免NPE。
一个非常常见的问题,我用谷歌collections,这里是我的代码
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; }
}
希望这可以帮助
您可以从列表中search项目如下function。 祝你好运!
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;
}