【问题标题】:Java HashSet vs Array PerformanceJava HashSet 与数组性能
【发布时间】:2013-09-09 20:55:24
【问题描述】:

我有一个保证是不同的对象的集合(特别是由唯一的整数 ID 索引)。我也确切地知道其中有多少(并且数量不会改变),并且想知道 Array 在存储/检索所述元素方面是否会比 HashSet 具有显着的性能优势。

在纸面上,Array 保证了恒定的时间插入(因为我提前知道了大小)和检索,但是 HashSet 的代码看起来更简洁并增加了一些灵活性,所以我想知道我是否会失去任何性能 -至少在理论上使用它是明智的。

【问题讨论】:

  • 你的数据集是稀疏的还是密集的?
  • HashSet 被设计为具有预期的恒定时间addcontainsremove 操作,这意味着无论集合中有多少元素,时间都不会发生太大变化。数组对所有这些都有线性操作,但开销较低。这意味着数组通常更适合小型集合。不久前,我在我的机器上使用 ArraySet 实现做了一些测试,发现使用 Array 而不是 Hash 通常最多使用 150 个元素会更好(但这在一定程度上取决于实现和操作:迭代是例如更快)。
  • 取决于你有多少项目EnumSet或类似的东西可能是一个选项。

标签: java arrays performance data-structures hashset


【解决方案1】:

取决于您的数据;

HashSet 为您提供 O(1) contains() 方法,但不保留顺序。

ArrayList contains() 是O(n),但您可以控制条目的顺序。

Array 如果您需要在两者之间插入任何内容,最坏的情况可能是 O(n),因为您必须将数据向下移动并为插入腾出空间。在Set,可以直接使用SortedSet which too has O(n) too but with flexible operations.

我相信 Set 更灵活。

【讨论】:

  • 但是TreeSetSortedSet的实现)是log(n)插入/查找...
  • @OliCharlesworth 德克萨斯州。强调 Sets 比 Array 更灵活。
【解决方案2】:

选择很大程度上取决于你想用它做什么。

如果是你的问题中提到的:

我有一个 集合 对象,这些对象保证是不同的(特别是,由唯一的整数 ID 索引)。我也确切地知道有多少

如果这是您需要做的,那么您两个都不需要。 Collection 中有一个 size() 方法,您可以通过该方法获取它的大小,这意味着 集合中有多少个

如果您所说的“对象集合”并不是真正的集合,并且您需要选择一种集合类型来存储您的对象以供进一步处理,那么您需要知道,对于不同类型的集合,有不同的能力和特点。

首先,我相信有一个公平的比较,你应该考虑使用 ArrayList 而不是 Array,你不需要处理重新分配。

然后就变成了ArrayList vs HashSet的选择,很直接:

您需要列表或集合吗?它们有不同的目的:列表为您提供索引访问,迭代按索引顺序排列。虽然 Set 主要是为了让您保留一组不同的数据,但鉴于其性质,您不会拥有索引访问权限。

当你决定使用 List 还是 Set 之后,就是 List/Set 实现的选择,通常 Lists 选择 ArrayList 和 LinkedList,而 Sets 选择 HashSet 和 TreeSet。

所有选择都取决于您希望如何处理该数据集合。他们在不同的动作上表现不同。

例如,ArrayList 中的索引访问是 O(1),HashSet 中(虽然没有意义)是 O(n),(只是为了您的兴趣,LinkedList 中是 O(n),TreeSet 中是 O(nlogn ) )

对于添加新元素,ArrayList 和 HashSet 都是 O(1) 操作。在中间插入对于 ArrayList 是 O(n),而在 HashSet 中没有意义。两者都会受到重新分配的影响,并且它们都需要 O(n) 进行重新分配(HashSet 通常重新分配较慢,因为它涉及再次计算每个元素的哈希)。

要查找某个元素是否存在于集合中,ArrayList 为 O(n),HashSet 为 O(1)。

还有很多操作可以做,不知道要做什么就讨论性能是没有意义的。

【讨论】:

    【解决方案3】:

    理论上,正如 SCJP6 学习指南所说:D

    数组比集合快,如前所述,大多数集合主要依赖于数组(Maps不被认为是Collection,但它们包含在Collections框架中)

    如果你保证你的元素的大小不会改变,为什么会卡在 Objects built on Objects(Collections built on Arrays),而你可以直接使用根对象(数组)

    【讨论】:

    • 因为如果您需要 O(1) 查找(包含),您将需要编写大量非平凡的代码。在这种情况下,问题就变成了:为什么要重新发明轮子?
    • 如果假设我需要存储 5 个字符串常量并在其中一个循环中解析相同的常量,我认为根据上述评论,数组更适合。请告诉我
    【解决方案4】:

    看起来您需要一个将 id 映射到计数的 HashMap。特别是,

    HashMap<Integer,Integer> counts=new HashMap<Integer,Integer>();
    counts.put(uniqueID,counts.get(uniqueID)+1);
    

    通过这种方式,您可以获得平摊的 O(1) 添加、包含和检索。本质上,与每个对象关联的具有唯一 ID 的数组是一个 HashMap。通过使用 HashMap,您可以获得额外的好处,即不必管理数组的大小,不必自己将键映射到数组索引和恒定的访问时间。

    【讨论】:

    • 或者一个HashSet,如果他使用的对象有一个hashCode方法返回它们的唯一标识符。请注意,这在实践中变化很小,因为HashSet 在内部使用了HashMap 的实例...
    猜你喜欢
    • 2014-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多