Java中Collection类的性能

时间:2010-10-19 22:54:54

标签: java performance collections

所有

我一直在浏览很多网站,这些网站发布各种各种行动的表现,包括添加元素,搜索和删除。但我也注意到它们都提供了不同的测试环境,即O.S,内存,线程运行等。

我的问题是,是否有任何网站/材料在最佳测试环境基础上提供相同的性能信息?即,配置不应成为任何特定数据结构性能不佳的问题或催化剂。

[Updated]:示例,HashSet和LinkedHashSet都具有插入元素的复杂度O(1)。但是,Bruce Eckel的测试声称,LinkedHashSet的插入时间比HashSet要多[http://www.artima.com/weblogs/viewpost.jsp?thread=122295]。那么我还应该使用Big-Oh表示法吗?

7 个答案:

答案 0 :(得分:9)

以下是我的建议:

  1. 首先,不要优化:)不是我告诉你设计垃圾软件,而是仅仅关注设计和代码质量而不是过早优化。假设你已经做到了,现在你真的需要担心哪个集合最好超出纯粹的概念原因,让我们继续前进到第2点
  2. Really, don't optimize yet(大致从M. A. Jackson被盗)
  3. 精细。所以你的问题是,即使你有最佳案例,最坏情况和平均案例的理论时间复杂度公式,你已经注意到人们说不同的东西,实际设置与理论完全不同。所以运行自己的基准测试!你只能阅读这么多,而当你这样做时,你的代码就不会自己写。完成理论后,编写自己的基准测试 - 针对您的实际应用程序,而不是用于测试目的的一些不相关的迷你应用程序 - 并查看您的软件实际发生了什么以及原因。然后选择最好的算法。这是经验性的,它可以被视为浪费时间,但它是实际上完美运作的唯一方式(直到你到达下一点)。
  4. 既然你已经做到了,那么你拥有最快的应用程序。直到下一次更新JVM。或者操作系统的某些底层组件,您的特定性能瓶颈取决于。你猜怎么着?也许你的客户有不同的。有趣的是:您需要确保您的基准测试对其他人或大多数情况有效(或者为不同的情况编写代码很有乐趣)。您需要从用户收集数据。手。然后你需要一遍又一遍地看看会发生什么,如果它仍然成立。然后一遍又一遍地重新编写代码(现在终止 - Engineering Windows 7 blog实际上是一个很好的例子,说明用户数据收集如何帮助做出有根据的决策来改善用户体验。
  5. 或者你可以......你知道......不优化。平台和编译器将会改变,但一个好的设计应该 - 平均 - 表现得足够好。

    您还可以做的其他事情:

    • 查看JVM的源代码。这是非常有教育意义的,你会发现一大堆隐藏的东西(我不是说你必须使用它们......)
    • 在TODO列表中查看您需要处理的其他内容吗?是的,靠近顶部的那个,但你总是跳过因为它太难或不够有趣。那一个就在那里。好了,让优化的东西独自一人:它是Pandora's Box和Moebius乐队的邪恶孩子。你永远不会摆脱它,你会深感遗憾的是你试图用它来。

    有人说,我不知道为什么你需要提升性能,所以也许你有一个非常有效的理由。

    我并不是说选择合适的收藏并不重要。只有那些你知道哪一个选择特定问题,并且你已经看过其他选择,那么你已经完成了你的工作,而不必感到愧疚。这些集合通常具有语义含义,只要你尊重它就可以了。

答案 1 :(得分:6)

在我看来,您需要了解的有关数据结构的所有操作都是对其进行操作的Big-O,而不是来自不同体系结构的主观测量。不同的收藏品有不同的用途。

Map是字典
Set断言唯一性 List提供分组并保留迭代顺序
Tree提供便宜的排序和快速搜索动态变化的内容,需要不断的排序

已编辑以包含bwawok关于树结构用例的声明

<强>更新
来自javadoc on LinkedHashSet

  

Set接口的哈希表和链表实现,具有可预测的迭代顺序。

     

...

     

由于维护链表的额外费用,性能可能略低于HashSet的性能,但有一个例外:对LinkedHashSet的迭代需要与集合大小成比例的时间,而不管其容量如何。对HashSet的迭代可能更昂贵,需要与其容量成比例的时间。

现在我们已经从选择适当的数据结构接口的一般情况转变为使用哪种实现的更具体的情况。但是,我们最终得出的结论是,基于每个实现提供的独特,微妙的不变量,特定实现非常适合特定应用程序。

答案 2 :(得分:5)

您需要了解他们,为什么?基准测试显示给定JDK和硬件设置的原因是它们(理论上)可以再现。你应该从基准测试得到的是对事物如何运作的想法。对于ABSOLUTE数字,您需要根据您自己的代码运行它。

最重要的是要了解各种集合的Big O运行时。知道从未排序的ArrayList中获取元素是O(n),但是从HashMap中获取它是O(1) HUGE

如果您已经为给定的工作使用了正确的收藏品,那么您将有90%的收入。当你需要担心从HashMap中获取项目的速度时,应该是非常罕见的。

一旦离开单线程域并进入多线程域,您将需要开始担心ConcurrentHashMap和Collections.synchronized hashmap之类的问题。在你是多线程之前,你可以不用担心这种东西,并专注于哪个集合使用。

更新到HashSet与LinkedHashSet

我还没有找到一个需要链接哈希集的用例(因为如果我关心顺序我倾向于有一个List,如果我关心O(1)获取,我倾向于使用HashSet。实际上,大多数代码都会使用ArrayList,HashMap或HashSet。如果你还需要其他任何东西,你就处于“边缘”状态。

答案 3 :(得分:4)

不同的集合类具有不同的大O表现,但所有这些都告诉你它们随着它们变大而如何扩展。如果你的集合足够大,那么O(1)的集合将胜过具有O(N)或O(logN)的集合,但除了实验之外,没有办法告诉N的什么值是盈亏平衡点。 / p>

一般来说,我只是使用最简单的东西,然后如果它成为一个“瓶颈”,正如该数据结构上的操作占用了大量的时间,那么我将切换到具有更好的大O评级的东西。通常情况下,集合中的项目数量永远不会接近收支平衡点,或者还有另一种简单的方法来解决性能问题。

答案 4 :(得分:1)

HashSetLinkedHashSet都有O(1)表现。与HashMapLinkedHashMap相同(实际上前者是基于后者实现的)。这只会告诉您这些算法 scale 的方式,而不是它们实际执行的方式。在这种情况下,LinkHashSet执行与HashSet完全相同的工作,但也始终必须更新上一个和下一个指针以维护订单。这意味着HashSet常量(在讨论实际算法性能时这是一个重要值)低于LinkHashSet

因此,由于这两个具有相同的Big-O,它们本质上相同 - 即,当 n 发生变化时,两者都具有相同的性能变化并且具有O(1)性能,平均来说,不会改变。

所以现在你的选择是基于功能和你的要求(无论如何,这应该是你首先考虑的)。如果您只需要快速添加获取操作,则应始终选择HashSet。如果您还需要一致的排序 - 例如上次访问或插入订单 - 那么必须也使用该类的Linked ...版本。

我在生产应用程序中使用了“链接”类,LinkedHashMap。我在一个案例中使用了这个符号,如表格,因此需要快速访问符号和相关信息。但我还想按照用户定义这些符号(插入顺序)的顺序在至少一个上下文中输出信息。这使输出对用户更友好,因为他们可以按照定义的顺序查找内容。

答案 5 :(得分:0)

如果我必须排序数百万行,我会尝试找到一种不同的方式。也许我可以改进我的SQL,改进我的算法,或者可能将元素写入磁盘并使用操作系统的sort命令。

我从未遇到过导致我的表现出现问题的收藏品。

答案 6 :(得分:0)

我使用HashSets和LinkedHashSets创建了自己的实验。对于add()和包含运行时间是O(1),没有考虑到很多冲突。在Linkedhashset的add()方法中,我将对象放在用户创建的哈希表中,该表是O(1),然后将对象放在单独的链表中以说明顺序。因此,从linkedhashset中删除元素的运行时间必须在哈希表中找到该元素,然后搜索具有该顺序的链表。因此运行时间分别为O(1)+ O(n),对于remove()

是o(n)