什么是最有效的Java Collections库?
几年前,我做了很多Java并且给人的印象是trove是最好的(最有效的)Java Collections实现。但是,当我阅读问题“Most useful free Java libraries?”的答案时,我注意到trove几乎没有被提及。那么哪个Java Collections库现在最好?
更新:为了澄清,我当然想知道当我必须在哈希表等中存储数百万个条目时要使用的库(需要小的运行时和内存占用)。
答案 0 :(得分:102)
问题是(现在)关于在Map中存储大量数据,这些数据可以使用原始类型(如int
来表示)。在我看来,这里的一些答案非常具有误导性。让我们看看为什么。
我从trove修改了基准来测量运行时和内存消耗。我还在此基准测试中添加了PCJ,这是基本类型的另一个集合库(我广泛使用它)。 “官方”宝库基准测试不会将IntIntMaps与Java Collection Map<Integer, Integer>
进行比较,可能存储Integers
并且从技术角度来看存储ints
不一样。但是用户可能不关心这个技术细节,他希望有效地存储可用ints
表示的数据。
首先是代码的相关部分:
new Operation() {
private long usedMem() {
System.gc();
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
// trove
public void ours() {
long mem = usedMem();
TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
ours.put(i, i);
}
mem = usedMem() - mem;
System.err.println("trove " + mem + " bytes");
ours.clear();
}
public void pcj() {
long mem = usedMem();
IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("pcj " + mem + " bytes");
map.clear();
}
// java collections
public void theirs() {
long mem = usedMem();
Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("java " + mem + " bytes");
map.clear();
}
我认为数据是原始的ints
,看起来很健全。但这意味着java util的运行时损失,因为自动装箱,这对于原始集合框架来说不是必需的。
WinXP上的运行时结果(当然没有gc()
调用),jdk1.6.0_10:
100000 put operations 100000 contains operations java collections 1938 ms 203 ms trove 234 ms 125 ms pcj 516 ms 94 ms
虽然这可能看起来很激烈,但这并不是使用这种框架的原因。
原因是内存性能。包含100000个int
条目的Map的结果:
java collections oscillates between 6644536 and 7168840 bytes trove 1853296 bytes pcj 1866112 bytes
与原始集合框架相比,Java集合需要三倍以上内存。即您可以在内存中保留三倍的数据,而无需借助磁盘IO来降低运行时性能的大小。这很重要。阅读highscalability以找出原因。
根据我的经验,高内存消耗是Java最大的性能问题,这当然也会导致运行时性能下降。原始集合框架在这里可以提供帮助。
所以:不,java.util不是答案。在询问效率时,向Java集合添加“功能”并不是重点。此外,现代JDK系列不“甚至超过专业的Trove系列”。
免责声明:此处的基准测试远未完成,也不完美。它旨在将我在许多项目中经历过的观点带回家。原始集合足以容忍可疑的API - 如果您使用大量数据。
答案 1 :(得分:72)
从检查来看,看起来Trove只是一个原始类型的集合库 - 它不像是在JDK中为普通集合添加了很多功能。
我个人(我有偏见)我喜欢Guava(包括以前的Google Java Collections项目)。它使各种任务(包括集合)变得更加容易,其方式至少相当有效。鉴于集合操作很少在我的代码中形成瓶颈(根据我的经验),这比集合API“更好”,集合API可能更有效但不会使我的代码可读。
鉴于Trove和Guava之间的重叠几乎为零,或许您可以从集合库中澄清您实际需要的内容。
答案 2 :(得分:45)
我知道这是一个老帖子,这里有很多答案。 但是,在建议图书馆方面,上面的答案是肤浅的,而且过于简化。没有一个图书馆在这里提供的各种基准测试中表现良好。我得出的唯一结论是,如果你关心性能和内存,特别是处理原始类型,那么它非常值得关注非jdk替代方案。
在基准力学和所涵盖的图书馆方面,这是一个更健全的分析。 This是mahout开发列表中的一个主题。
所涵盖的图书馆是
2015年6月更新: 不幸的是,原来的基准测试不再可用,而且有点过时了。 Here是由其他人完成的最新(2015年1月)基准测试。它不是那么全面,也没有原始链接的交互式探索工具。
答案 3 :(得分:19)
正如其他评论员所注意到的那样,“高效”的定义投入了广泛的网络。然而,还没有人提到Javolution library。
一些亮点:
Javolution发行版包含一个基准测试套件,因此您可以看到它们如何与其他库/内置集合进行叠加。
答案 4 :(得分:15)
要考虑的一些集合库:
我首先要到达JDK集合库。它涵盖了您需要做的最常见的事情,显然已经可以使用了。
Google Collections可能是JDK之外最好的高质量库。它使用频繁且得到很好的支持。
Apache Commons Collections年龄较大,受到“太多厨师”问题的影响,但也有很多有用的东西。
Trove拥有非常专业的集合,适用于原始键/值等案例。现在,我们发现在现代JDK和Java 5+集合以及并发用例中,JDK集合甚至超过了专门的Trove集合。
如果你有很高的并发用例,你一定要在高规模的lib中检查像NonBlockingHashMap这样的东西,这是一个无锁的实现,如果你有合适的用例,它可以踩踏ConcurrentHashMap。
答案 5 :(得分:6)
<强> java.util
强>
很抱歉显而易见的答案,但对于大多数用途,默认Java Collections绰绰有余。
答案 6 :(得分:6)
要在地图中存储数百万String
,请查看http://code.google.com/p/flatmap
答案 7 :(得分:4)
我是来自happy-collections on source-forge
的快乐收藏品的开发者答案 8 :(得分:3)
ConcurrentHashMap以及java.util.concurrent
包。因为这是标准java的一部分,所以会有很小的内存占用。
答案 9 :(得分:3)
取决于我们如何定义“高效”。
每个数据结构都有自己的Big-Oh行为,用于读取,写入,迭代,内存占用等。一个库中的链表可能与其他任何一个相同。读取O(1)的哈希映射比链表O(n)更快。
但是当我读到“最有用的免费Java库?”这个问题的答案时。我注意到很难提到这个问题。
这听起来不像“最有效”。这对我来说听起来像“最受欢迎”。
只是一些反馈 - 我从来没有听说过,而且我不知道有谁使用过它。 JDK,Google或Apache Commons中内置的集合对我来说是众所周知的。
答案 10 :(得分:3)
Trove提供了一些优势。
也就是说,自从编写了trove之后,已经做了很多工作来改进jdk集合。
它的散列策略让我对它很有吸引力... Google for trove并阅读他们的概述。
答案 11 :(得分:2)
如果要在哈希表中存储数百万条记录,可能会遇到内存问题。例如,当我尝试创建一个包含230万个String对象的地图时,就发生了这种情况。我和BerkeleyDB一起去了,这是非常成熟的,表现很好。他们有一个包装Collections API的Java API,因此您可以轻松创建任意大的映射,而内存占用很少。但访问速度会慢(因为它存储在磁盘上)。
后续问题:对于不可变集合,是否有一个体面(且高效),维护良好的库? Clojure对此有很好的支持,为Java提供类似的东西会很好。