【发布时间】:2010-09-10 17:47:15
【问题描述】:
假设您有一个包含数百个内存对象的集合,并且您需要查询此 List 以返回匹配某些 SQL 或 Criteria 类查询的对象。例如,您可能有一个 Car 对象列表,并且您想要返回 1960 年代制造的所有汽车,车牌以 AZ 开头,按车型名称排序。
我知道JoSQL,有没有人用过这个,或者对其他/本土解决方案有任何经验?
【问题讨论】:
标签: java sql collections
假设您有一个包含数百个内存对象的集合,并且您需要查询此 List 以返回匹配某些 SQL 或 Criteria 类查询的对象。例如,您可能有一个 Car 对象列表,并且您想要返回 1960 年代制造的所有汽车,车牌以 AZ 开头,按车型名称排序。
我知道JoSQL,有没有人用过这个,或者对其他/本土解决方案有任何经验?
【问题讨论】:
标签: java sql collections
是的,我知道这是一篇旧帖子,但技术每天都在出现,答案会随着时间而改变。
我认为这是一个用 LambdaJ 解决的好问题。你可以在这里找到它: http://code.google.com/p/lambdaj/
这里有一个例子:
寻找活跃客户//(可迭代版本)
List<Customer> activeCustomers = new ArrayList<Customer>();
for (Customer customer : customers) {
if (customer.isActive()) {
activeCusomers.add(customer);
}
}
LambdaJ 版本
List<Customer> activeCustomers = select(customers,
having(on(Customer.class).isActive()));
当然,有这种美感会影响性能(有点……平均2倍),但是你能找到更可读的代码吗?
它有很多功能,另一个例子可能是排序:
排序迭代
List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
}
});
使用 lambda 排序
List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge());
更新:在 java 8 之后,您可以使用开箱即用的 lambda 表达式,例如:
List<Customer> activeCustomers = customers.stream()
.filter(Customer::isActive)
.collect(Collectors.toList());
【讨论】:
过滤是执行此操作的一种方法,如其他答案中所述。
过滤是不可扩展的。从表面上看,时间复杂度似乎是 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)(在工程意义上,请参见下面的 cmets)。将其他对象添加到集合中时,性能不会降低那么多。但这仍然不完美,请继续阅读。
另一种方法,我称之为常设查询索引。解释一下:通过传统的迭代和过滤,对集合进行迭代并测试每个对象以查看它是否与查询匹配。所以过滤就像在一个集合上运行一个查询。常设查询索引则相反,集合在查询上运行,但对于集合中的每个对象仅运行一次,即使该集合可以被查询任意次数。
常设查询索引就像用某种智能集合注册查询,这样当对象被添加到集合中或从集合中移除时,集合将根据已注册的所有常设查询自动测试每个对象。如果对象与常设查询匹配,则集合可以将其添加到/从专用于存储与该查询匹配的对象的集合中删除。随后,可以在 O(1) 时间复杂度内检索匹配任何已注册查询的对象。
以上信息取自CQEngine (Collection Query Engine)。这基本上是一个 NoSQL 查询引擎,用于使用类似 SQL 的查询从 Java 集合中检索对象,而无需遍历集合的开销。它是围绕上述想法构建的,还有更多。免责声明:我是作者。它是开源的,位于 Maven 中心。 如果你觉得有帮助,请点赞这个答案!
【讨论】:
继续Comparator 主题,您可能还想看看Google Collections API。特别是,它们有一个名为Predicate 的接口,它的作用与Comparator 相似,因为它是一个可以被过滤方法使用的简单接口,例如Sets.filter。它们包括一大堆复合谓词实现,用于执行 AND、OR 等。
根据数据集的大小,使用这种方法可能比 SQL 或外部关系数据库方法更有意义。
【讨论】:
Comparator 选项还不错,尤其是如果您使用匿名类(以免在项目中创建冗余类),但最终当您查看比较流程时,它几乎就像循环遍历自己整个集合,准确指定匹配项目的条件:
if (Car car : cars) {
if (1959 < car.getYear() && 1970 > car.getYear() &&
car.getLicense().startsWith("AZ")) {
result.add(car);
}
}
然后是排序......这可能是一个痛苦的背后,但幸运的是有类Collections及其sort方法,其中一个接收Comparator...
【讨论】:
我在生产应用程序中使用了Apache Commons JXPath。它允许您将 XPath 表达式应用于 Java 中的对象图。
【讨论】:
如果你需要一个具体的匹配,你可以让类实现 Comparator,然后创建一个包含所有散列字段的独立对象,并使用它来返回匹配的索引。当您想在集合中找到多个(潜在的)对象时,您将不得不求助于像 JoSQL 这样的库(它在我使用它的琐碎案例中运行良好)。
一般来说,我倾向于将 Derby 嵌入到我的小型应用程序中,使用 Hibernate 注释来定义我的模型类,并让 Hibernate 处理缓存方案以保持一切快速。
【讨论】:
我会使用一个比较器,它需要一系列年份和车牌模式作为输入参数。然后只需遍历您的集合并复制匹配的对象。您最终可能会使用这种方法制作一整套自定义比较器。
【讨论】: