【问题标题】:Does HashSet preserve insertion order?HashSet 是否保留插入顺序?
【发布时间】:2010-10-14 00:34:09
【问题描述】:

.NET 3.5 中引入的HashSet 集合在使用foreach 进行迭代时是否保留插入顺序?

documentation 声明该集合未排序,但它没有说明插入顺序。预发布 BCL blog entry 声明它是无序的,但 this article 声明它旨在保留插入顺序。我有限的测试表明,该顺序被保留,但这可能是巧合。

【问题讨论】:

标签: .net hashset


【解决方案1】:

我认为声称它保留排序的文章是完全错误的。对于简单的测试,由于内部结构可能会很好地保留插入顺序,但不能保证并且不会总是以这种方式工作。我会试着想出一个反例。

编辑:这是反例:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        var set = new HashSet<int>();

        set.Add(1);
        set.Add(2);
        set.Add(3);
        set.Remove(2);
        set.Add(4);


        foreach (int x in set)
        {
            Console.WriteLine(x);
        }
    }
}

尽管在 4 之前插入了 3,但仍会打印 1、4、3。

可能如果您从不删除任何项目,它将保留插入顺序。我不确定,但我不会完全感到惊讶。但是,我认为依赖它是一个非常糟糕的主意:

  • 没有记录以这种方式工作,并且文档明确指出它没有排序。
  • 我没有查看过内部结构或源代码(显然我没有) - 在坚定地提出任何此类声明之前,我必须仔细研究它们。
  • 实现可以很容易地在框架版本之间进行更改。依靠这一点就像依靠string.GetHashCode 实现不改变——有些人在.NET 1.1 天就这样做了,然后当.NET 2.0 中的实现确实改变时他们被烧毁了。 ..

【讨论】:

  • 这也是我的假设。不幸的是,其他文章声称相同(基于所述文章)。最好能从可靠来源获得明确的是/否答案。
  • 尽管有官方文档,但关于此的大量错误信息让我有些震惊。还发现这个页面ezinearticles.com/?C-HashSet-Advantages&id=1761474 在谷歌搜索中也很高。更糟糕的是,它特别承认有两种不同类型的集合实现:那些保持顺序和不保持顺序,但它特别声称在 .NET 中 HashSet 确实保持顺序。
  • foreach 不按顺序迭代。始终使用“for”和索引。
  • @MihaiBratulescu:它以任何对MoveNext 的调用返回的顺序进行迭代。对于我知道的每种有序类型,这将与使用索引的顺序相同。请注意,在有问题的类型 (HashSet&lt;T&gt;) 中 没有 没有索引器。你能举一个具体的例子,你认为使用索引比使用 foreach 循环更好吗?
【解决方案2】:

This HashSet MSDN page具体说:

集合是不包含重复元素且其元素没有特定顺序的集合。

【讨论】:

  • HashSet 暗示它基于哈希表。哈希表顺序主要取决于集合中项目的哈希码,而不是插入顺序。
  • 同意。有关反例,请参阅 Jon Skeet 的答案。这是一个相关的问题,询问有关此类 HashTable 的实现 - 如果您想保证保留插入顺序。 stackoverflow.com/questions/1552225/…
  • @BrianRasmussen 哈哈...只需在 MSDN 中阅读并在此处导航以防万一...+1 不再浪费我的时间
  • 回答问题!
  • “其元素没有特定顺序”
【解决方案3】:

文档说明:

A HashSet)>) 集合未排序且不能包含重复元素。如果顺序或元素重复对应用程序而言比性能更重要,请考虑将 List)>) 类与 Sort 方法一起使用。

因此,它是否真的保留当前实现中元素的顺序并不重要,因为它没有记录为这样做,即使现在看起来这可能在未来的任何时候发生变化(即使在框架的修补程序)。

您应该根据书面合同进行编程,而不是实施细节

【讨论】:

  • 我同意,但我认为上面的引用不足以传达信息。我很确定,这个集合是无序的,我只是在寻找一些清晰的文档。
【解决方案4】:

不,哈希集不会保留插入顺序,至少不可预测。您可以使用 LinkedHashSet (Java) 或等效的。 LinkedHashSet 将保持顺序。

如果你想要排序,你甚至不应该首先使用集合...它不是为有序元素制作的,除非在特殊情况下。

编辑:听起来我在说教:-/对不起。

【讨论】:

  • 我不是想那样使用 HashSet,我是想阻止同事这样做。
  • 啊,好吧......我们的项目在这里遇到了同样的问题。不过,这是必要的,因为我们想要一个必须包含独特项目的有序列表。
【解决方案5】:

专门有一个SortedSet&lt;T&gt; collection in .NET4

这会给你排序,但不太可能是插入顺序排序。由于您可以使用自定义 IComparer,因此理论上您可以让它做任何事情。

【讨论】:

    【解决方案6】:

    阅读HashSet.AddIfNotPresent 的源代码,您可以看到插入顺序被保留假设没有任何删除

    因此new HashSet&lt;string&gt; { "Tom", "Dick", "Harry" } 保留了顺序,但如果您随后删除 Dick 并添加 Rick,则顺序将为 ["Tom", "Rick", "Harry"]。

    【讨论】:

    • 同意。只要您从不删除项目,它们就会按插入顺序枚举。这可能是一个有用的属性,即使没有记录,也不会从类中删除。团队不会冒险破坏依赖这种行为的应用程序。
    猜你喜欢
    • 2018-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 2014-02-01
    相关资源
    最近更新 更多