如何在Java中查询对象集合(Criteria / SQL-like)?

假设您拥有几百个内存中对象的集合,并且您需要查询此List以返回与某些SQL或条件匹配的对象(如查询)。 例如,您可能有一个汽车列表对象,并且您希望返回在20世纪60年代制造的所有汽车,其中车牌以AZ开头,按车型名称排序。

我知道JoSQL ,有没有人使用过它,或者有其他/本土解决方案的经验?

我在生产应用程序中使用了Apache Commons JXPath 。 它允许您将XPath表达式应用于Java中的对象图。

过滤是实现此目的的一种方式,如其他答案中所述。

虽然过滤不可扩展。 从表面上看,时间复杂度似乎是O( n )(即,如果集合中的对象数量增长,则已经不可扩展),但实际上是因为需要根据查询,时间对每个对象应用一个或多个测试复杂度更准确的是O( nt ),其中t是应用于每个对象的测试数。

因此,随着其他对象添加到集合中和/或查询中的测试数量增加,性能将下降。

还有另一种方法可以使用索引和集合理论。

一种方法是在存储在集合中的对象中字段上构建索引然后在查询中对其进行测试。

假设您有Car对象的集合,并且每个Car对象都有一个字段color 。 假设您的查询相当于“ SELECT * FROM cars WHERE Car.color = 'blue' ”。 你可以在Car.color上建立一个索引,它基本上是这样的:

 'blue' -> {Car{name=blue_car_1, color='blue'}, Car{name=blue_car_2, color='blue'}} 'red' -> {Car{name=red_car_1, color='red'}, Car{name=red_car_2, color='red'}} 

然后给出查询WHERE Car.color = 'blue' ,可以在O( 1 )时间复杂度中检索蓝色汽车组。 如果您的查询中有其他测试,则可以测试该候选集中的每辆车,以检查它是否与查询中的其余测试相匹配。 由于候选集可能明显小于整个集合,因此时间复杂度小于 O( n )(在工程意义上,请参见下面的评论)。 将其他对象添加到集合时,性能不会降低太多 。 但这仍然不完美,请继续阅读。

另一种方法是我将其称为常设查询索引 。 解释:使用传统的迭代和过滤,迭代集合并测试每个对象以查看它是否与查询匹配。 因此,过滤就像在集合上运行查询一样。 站点查询索引将是另一种方式,其中集合反而在查询上运行,但对于集合中的每个对象只运行一次,即使可以多次查询集合。

常设查询索引就像使用某种智能集合来注册查询,这样,当对象被添加到集合中或从集合中移除时,集合将自动针对已经向其注册的所有常设查询来测试每个对象。 如果对象与常设查询匹配,则该集合可以向/从专用于存储与该查询匹配的对象的集合添加/移除它。 随后,可以在O( 1 )时间复杂度中检索匹配任何已注册查询的对象。

以上信息来自CQEngine(Collection Query Engine) 。 这基本上是一个NoSQL查询引擎,用于使用类似SQL的查询从Java集合中检索对象,而无需迭代集合。 它围绕上面的想法,再加上一些。 免责声明:我是作者。 它是开源的,并且在maven中心。 如果你觉得它有用,请upvote这个答案!

是的,我知道这是一个老post,但技术每天都会出现,答案会随着时间的推移而改变。

我认为使用LambdaJ解决它是一个很好的问题。 您可以在此处找到它: http : //code.google.com/p/lambdaj/

这里有一个例子:

寻找活跃的客户//(可交换的版本)

 List activeCustomers = new ArrayList(); for (Customer customer : customers) { if (customer.isActive()) { activeCusomers.add(customer); } } 

LambdaJ版本

 List activeCustomers = select(customers, having(on(Customer.class).isActive())); 

当然,拥有这种美感会影响性能(有点……平均2次),但是你能找到更易读的代码吗?

它有许多function,另一个例子可能是排序:

排序迭代

 List sortedByAgePersons = new ArrayList(persons); Collections.sort(sortedByAgePersons, new Comparator() { public int compare(Person p1, Person p2) { return Integer.valueOf(p1.getAge()).compareTo(p2.getAge()); } }); 

用lambda排序

 List sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

继续使用Comparator主题,您可能还需要查看Google Collections API。 特别是,它们有一个名为Predicate的接口,它与Comparator具有相似的作用,因为它是一个简单的接口,可以被过滤方法使用,比如Sets.filter 。 它们包括一大堆复合谓词实现,用于执行AND,OR等。

根据数据集的大小,使用此方法比使用SQL或外部关系数据库方法更有意义。

如果需要单个具体匹配,则可以让类实现Comparator,然后创建一个包含所有散列字段的独立对象,并使用它来返回匹配的索引。 当你想在集合中找到多个(可能的)对象时,你将不得不求助于像JoSQL这样的库(它在我用过它的琐碎案例中运行良好)。

一般来说,我倾向于将Derby嵌入到我的小应用程序中,使用Hibernate注释来定义我的模型类,让Hibernate处理缓存方案以保持一切快速。

我会使用一个比较年份和车牌模式作为输入参数的比较器。 然后只需遍历您的集合并复制匹配的对象。 您最终可能会使用这种方法制作一整套自定义比较器。

Comparator选项也不错,特别是如果你使用匿名类(以便不在项目中创建冗余类),但最终当你看到比较流程时,它就像自己循环遍历整个集合一样,指定确切匹配项目的条件:

 if (Car car : cars) { if (1959 < car.getYear() && 1970 > car.getYear() && car.getLicense().startsWith("AZ")) { result.add(car); } } 

然后是排序……这可能是背后的痛苦,但幸运的是有类Collections及其sort方法,其中一个接收Comparator ……