什么是最有效的Java Collections库?[关闭]


135

什么是最有效的Java Collections库?

几年前,我做了很多Java的工作,给人留下的印象是那个宝库是最好(最有效)的Java Collections实现。但是,当我阅读“ 最有用的免费Java库? ” 这个问题的答案时,我注意到几乎没有提到trove。那么,哪个Java Collections库现在最好?

更新:为澄清起见,我主要想知道当我必须在哈希表等中存储数百万个条目时使用哪个库(需要小的运行时和内存占用)。


该表中的键和值是什么?如果它们不是基元,那么普通的HashMap等怎么了?
乔恩·斯基特

对于非常大的地图,您可能需要探测实现,甚至像数据库表一样内联。
Tom Hawtin-抢险

1
有趣的是,我在这里没有提到柯尔特,而柯尔特后来被归入Mahout。
smartnut007 '02

4
值得一提的是非常好的集合库-GS集合(github.com/goldmansachs/gs-collections)。它具有出色的文档资料和详尽的可
变性

Answers:


73

从检查的角度看,Trove似乎只是原始类型的集合的库-并不是要在JDK的正常集合上添加很多功能。

就个人而言(我有偏见),我喜欢Guava(包括以前的Google Java Collections项目)。它以一种至少相当有效的方式简化了各种任务(包括集合)。考虑到收集操作很少会形成代码瓶颈(以我的经验),这比收集API“更好”,后者可能更有效,但不会使我的代码具有可读性。

考虑到Trove和Guava之间的重叠几乎为零,也许您可​​以从收藏库中弄清楚您真正想要的东西。


3
@Andreas:不能说我同意。并不是说这是一个“一个或另一个”方案-我使用常规集合(带有Lists类之类的帮助器),然后在需要时使用Iterables等。仅在帮助您时使用复杂性。
乔恩·斯基特

10
在广泛使用GC几个月后,阅读了我自己的评论之后-我不同意过去的观点,并完全同意您的观点。广泛使用辅助方法/类,它们使许多代码更易读和更安全。
安德烈亚斯·彼得森,

1
@Andreas:感谢您回来并这么说-我很高兴听到GJC正在帮助:)
Jon Skeet

2
嘿,乔恩,Google Java Collections现在是Guava了。您可能需要更新您的帖子以供将来参考:)
Artur Czajka 2011年

1
我参与了很多数据密集型项目,其中集合是一个巨大的瓶颈。Java集合效率极低(包括内存和速度),特别是如果它们存储原语。
杰·阿斯伦

104

现在的问题是关于存储大量数据,这些数据可以使用诸如 int Map中的。在我看来,这里的一些答案是非常令人误解的。让我们看看为什么。

我从trove修改了基准,以测量运行时和内存消耗。我还将PCJ添加到该基准测试中,这是另一个用于原始类型的集合库(我广泛使用了该库)。“正式”的trove基准测试没有将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的运行时惩罚,因为自动装箱对于原始集合框架不是必需的。

gc()在WinXP jdk1.6.0_10上的运行时结果(当然没有调用):

                      100000放置操作100000包含操作 
Java集合1938毫秒203毫秒
234毫秒125毫秒
pcj 516毫秒94毫秒

尽管这似乎已经很严峻,但这并不是使用这种框架的原因。

原因是内存性能。包含100000 int个条目的Map的结果:

Java集合在6644536和7168840字节之间振荡
trove 1853296字节
pcj 1866112字节

与原始集合框架相比,Java集合需要的内存是其三倍以上。也就是说,您可以在内存中保留三倍的数据,而无需诉诸于磁盘IO,这会严重降低运行时性能。这很重要。阅读可扩展性以了解原因。

以我的经验,高内存消耗是Java的最大性能问题,这当然也会导致运行时性能下降。原始收集框架确实可以在这里提供帮助。

因此:不,java.util不是答案。在询问效率时,向Java集合“添加功能”并不是重点。同样,现代的JDK集合也不 “超越专业的Trove集合”。

免责声明:这里的基准还远远不够完善,也不是完美的。我要在很多项目中都经历过这一点。如果您处理大量数据,原始集合足够有用以容忍可疑的API 。


3
实际上,我认为您的答案是误导性的。存储整数与整数之间的区别非常大,这很可能是内存使用率增加的主要原因。我同意原始类型收集框架可能会有用,但是它不会使trove或pcj比java.util更好。
乔恩

22
问题是关于有效存储int数据。与存储整数无关。正如我试图展示的那样,对于trove / pcj来说,它的效率更高。使用整数会导致运行时和内存效率低下。由于java.util不允许使用基元,因此它不是此任务的最佳选择。
the.duckman

2
(俄语社区)在这里不用的又一标杆:total-holywar.blogspot.com/2011/07/...
dma_k

不知道我们是否不使用int作为键,只是普通的String。他们的工作台结果是什么?
克拉克·鲍

@ClarkBao(很抱歉,迟到了)将任何对象存储为键将使用object hashCode()。它使您int成为关键。
Matthieu 2014年

47

我知道这是一篇旧文章,并且这里有很多答案。但是,上述建议是肤浅的,在建议图书馆方面过于简化。在这里介绍的各种基准测试中,没有一个库能很好地完成工作。我得出的唯一结论是,如果您关心性能和内存,尤其是处理原始类型,则值得考虑使用非jdk替代方案。

就基准机制和所涵盖的库而言,这是一个更合理的分析。 是mahout开发人员列表中的一个线程。

涵盖的库是

  • 高性能计算机
  • 宝藏
  • 快速使用
  • 马豪(柯尔特)
  • Java集合

2015年6月更新:不幸的是,原始基准不再可用,而且有点过时了。 是其他人最近完成的基准测试(2015年1月)。它不像原始链接那样全面,也没有交互式探索工具。


1
谢谢。考虑到问题的重要性,这非常有帮助。很难相信没有其他答案(鸭人的答案)能真正回答这个问题。
德克斯特(Dexter)2012年

20

正如其他评论员所注意到的那样,“有效”的定义产生了广泛的影响。但是还没有人提到Javolution库

一些亮点:

  • Javolution类非常快速(例如,O [Log(n)]中的文本插入/删除,而不是标准StringBuffer / StringBuilder的O [n])。
  • 所有Javolution类都符合实时性,并具有高度确定性的行为(在微秒范围内)。此外(与标准库不同),Javolution是RTSJ安全的(与Java Real-Time扩展一起使用时,不会发生内存冲突或内存泄漏)。
  • Javolution的实时收集类(地图,列表,表和集合)可以代替大多数标准收集类并提供附加功能。
  • Javolution集合提供并发保证,以简化并行算法的实现。

Javolution发行版包括一个基准套件,因此您可以查看它们如何与其他库/内置集合进行堆叠。


16

需要考虑的一些收集库:

我首先要接触JDK集合库。它涵盖了您需要做的最常见的事情,并且显然已经为您所用。

Google馆藏可能是JDK之外最好的高质量库。它得到了广泛的使用和良好的支持。

Apache Commons Collections比较老,并且受“厨师太多”问题的影响,但也有很多有用的东西。

Trove对于诸如原始键/值之类的案例有非常专业的集合。这些天来,我们发现在现代JDK上以及Java 5+集合和并发用例中,JDK集合甚至胜过专门的Trove集合。

如果您确实有很高的并发用例,则绝对应该在高级lib中检出NonBlockingHashMap之类的东西,它是无锁的实现,如果您有合适的用例,可以在ConcurrentHashMap上sto脚。


7
“这些天,我们发现在现代JDK上以及Java 5+集合和并发用例中,JDK集合甚至优于专门的Trove集合。” 误导性-我从未见过这样的微基准测试:在像Trove这样的专用原始集合类中存储/检索原始类型在内存使用率和CPU时间方面均不胜过JDK集合类。如果您使用的是对象(而不是原始类型),那么我会同意Alex的观点,对collection impl的担心并不那么重要。
里亚德·卡拉

2
该声明是基于大量实际使用情况(我每天都会接管微基准测试)而得出的,其中包括我们以前需要Trove收集但现在可以将其取出的各种收集提示。JDK 6的最新更新(大约在2009年末)实际上为通用映射键(如Integer)提供了自定义代码,从而大大改善了某些最常用的用法。
亚历克斯·米勒

1
Alex,在您的特定用例中,我毫不怀疑拔出原始集合并使用JDK集合足够快,但是您会在集合的整个领域挥舞着手,然后说:“所有人,这足够快! ” 不准确。如果我使用的是2D游戏引擎,则对原始类型进行装箱/拆箱的开销会非常昂贵。如果我正在使用REST API,则不会,就HTTP I / O等更昂贵的操作而言,它可能根本没有可测量的不同。我只是觉得不得不量化您的帖子而已。
卡拉

4
我认为阅读此书的任何人都不应该听我们中的任何一个。他们应该测试自己的用例,看看哪些具有最佳性能。我的评论基于我的团队对各种库进行的相当积极的性能测试。YMMV。
亚历克斯·米勒

2
我同意@Riyad。我正在编写一个高性能的有限自动机套件,并已在Trove和Java Collections Framework(jdk 6最新更新)中实现了它。Trove胜过重要时刻。计算速度和内存消耗都提高了数十倍。
Nico Huysamen,2011年

6

java.util

很抱歉,答案很明显,但是对于大多数用途,默认的Java Collections绰绰有余。


4
对于基本用途,是的。但我认为,框架错过其中(例如)谷歌集合进来的一些基本和高级功能(如不可变的集合,过滤器,屈德宁等),这就是
乔恩

1
我认为这个答案没有重点。当人们很少使用Java时,JCF可能在2002年很棒。不幸的是,它还没有很好地老化,特别是与其他JVM语言的集合支持相比。
特德·彭宁斯

3
-1这个问题“对于存储int最有效”,任何提到的示例都比java.util好
kommradHomer 2013年




3

取决于我们如何定义“效率”。

每个数据结构在读取,写入,迭代,内存占用等方面都有其自己的Big-Oh行为。一个库中的链表可能与其他库相同。并且散列映射将比链接列表O(n)更快地读取O(1)。

但是,当我阅读“最有用的免费Java库”这个问题的答案时,我注意到几乎没有提到trove。

这听起来不像“最有效”。对我来说,这听起来像是“最受欢迎”。

只是一些反馈-我从未听说过,也不知道有人使用过。JDK,Google或Apache Commons中内置的集合对我来说是众所周知的。


3

Trove提供了一些优势。

  • 较小的内存占用,它不使用Map.Entry对象
  • 您可以使用散列策略来代替映射键,这样可以节省内存,这意味着您不必每次都想在一组新的属性上缓存对象时都定义一个新键
  • 它具有原始集合类型
  • 认为它具有某种形式的内部迭代器

也就是说,自trove编写以来,已经做了很多工作来改善jdk集合。

正是散列策略使它吸引了我……Google搜寻并阅读了它们的概述。


2

如果要在哈希表中存储数百万条记录,则很可能会遇到内存问题。例如,当我尝试使用230万个String对象创建地图时,这发生了。我去了BerkeleyDB,它非常成熟并且性能良好。它们具有包装Collections API的Java API,因此您可以轻松创建占用很少内存的任意大地图。但是访问速度会变慢(因为它存储在磁盘上)。

后续问题:是否有一个像样的(高效的),维护良好的,用于不可变集合的库?Clojure为此提供了出色的支持,并且对Java有类似的支持也将是一件很高兴的事情。


1
Google收藏集会添加不可变的收藏集。
the.duckman 2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.