HashSet是否保留插入顺序?


70

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

文档的状态,该集合没有排序,但它并没有说明插入顺序什么。预发布的BCL博客条目指出该条目是无序的,但本文指出该条目旨在保留插入顺序。我的有限测试表明,该顺序得以保留,但这可能是一个巧合。


在我的计算机上new HashSet<int>() { 6, 8 }.ToList()返回[6,8]但new HashSet<int>() { 8, 6 }.ToList()返回[8,6]
Panic Panic


如果您需要这样的收藏,请看一下:github.com/OndrejPetrzilka/Rock.Collections
Ondrej Petrzilka,2016年

Answers:


80

这个HashSet MSDN页面专门说:

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


3
HashSet表示它基于哈希表。哈希表顺序主要取决于集合中项目的哈希码,而不取决于插入顺序。
2009年

同意。有关反例,请参阅Jon Skeet的答案。这是一个有关此类HashTable的实现的相关问题-如果您要保证保留插入顺序。 stackoverflow.com/questions/1552225/...
乔治Mamaladze

@BrianRasmussen哈哈...只是在MSDN中阅读并导航到这里,以防万一... + 1不会浪费我的时间
Squ1rr3lz 2015年

回答问题!
–'OuuGiii

“其元素没有特定顺序”
迈克尔·伯尔

44

我认为声称它保留顺序的文章是完全错误的。对于简单的测试,由于内部结构的缘故,插入顺序可能会得到保留,但并不能保证一定会始终如此。我将尝试提出一个反例。

编辑:这是反例:

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,该页面在Google搜索中也很高。更糟糕的是,它明确地认识到有两种不同类型的set实现:可以保留和不保留顺序的那些,但是它特别声称在.NET中HashSet确实保留了顺序。
戴维

foreach不按顺序迭代。始终使用“ for”和索引。
Mihai Bratulescu,

@MihaiBratulescu:以MoveNext返回调用的任何顺序进行迭代。对于我所知道的每种有序类型,这将与使用索引的顺序相同。请注意,在所讨论的类型(HashSet<T>没有索引器。您能否举一个具体的例子,您认为使用索引比使用foreach循环更好?
乔恩·斯基特

7

该文档指出:

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

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

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


我同意,但是我认为上面的引用不足以传达信息。我非常确定,该集合将是无序的,我只是在寻找一些清晰的文档。
布赖恩·拉斯穆森


2

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

如果您要订购,则甚至根本不应该使用集合……除非特殊情况,否则它不是为有序元素而设计的。

编辑:听起来像我在宣讲:-/对不起。


1
我不是在试图以这种方式使用HashSet,而是试图防止同事这样做。
布赖恩·拉斯穆森

嗯,好吧...我们在这里的项目中遇到了同样的问题。但是,这是必要的,因为我们想要一个必须包含唯一项的有序列表。
Sudhir Jonathan

2

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

这样可以new HashSet<string> { "Tom", "Dick", "Harry" }保留顺序,但是如果您随后删除Dick并添加Rick,则顺序将为[“ Tom”,“ Rick”,“ Harry”]。


同意 只要您从不删除任何项目,它们就会按插入顺序枚举。这可能是有用的属性,即使没有记录,也不会从类中删除。团队不会冒险破坏依赖于这种行为的应用程序。
Drew Noakes
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.