如何在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
……