76

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

文档指出,该集合未排序,但没有说明插入顺序。一个预发布的 BCL博客条目声明它是无序的,但这篇文章声明它旨在保留插入顺序。我有限的测试表明,该顺序被保留,但这可能是巧合。

4

6 回答 6

87

这个 HashSet MSDN 页面特别说:

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

于 2009-03-18T08:03:12.557 回答
46

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

编辑:这是反例:

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。

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

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

该文档指出:

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

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

您应该根据记录的合同而不是实施细节进行编程。

于 2009-03-18T08:04:17.577 回答
3

.NET4中专门有一个SortedSet<T>集合

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

于 2012-03-19T19:43:29.347 回答
2

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

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

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

于 2009-03-18T09:11:19.397 回答
2

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

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

于 2016-04-26T15:52:27.220 回答