Behält HashSet die Einfügereihenfolge bei?

70

Behält die HashSetin .NET 3.5 eingeführte Sammlung die Einfügereihenfolge bei, wenn sie mit iteriert wird foreach?

In der Dokumentation heißt es, dass die Sammlung nicht sortiert ist, aber nichts über die Einfügereihenfolge aussagt. In einem BCL- Blogeintrag vor der Veröffentlichung wird angegeben , dass er ungeordnet ist. In diesem Artikel wird jedoch angegeben , dass die Einfügereihenfolge beibehalten werden soll. Meine begrenzten Tests legen nahe, dass die Reihenfolge erhalten bleibt, aber das könnte ein Zufall sein.

Brian Rasmussen
quelle
Auf meinem Computer new HashSet<int>() { 6, 8 }.ToList()kehrt [6,8] zurück, aber new HashSet<int>() { 8, 6 }.ToList()[8,6]
Colonel Panic
Wenn

Antworten:

80

Auf dieser HashSet-MSDN-Seite heißt es speziell:

Ein Satz ist eine Sammlung, die keine doppelten Elemente enthält und deren Elemente in keiner bestimmten Reihenfolge vorliegen.

Michael Burr
quelle
3
HashSet impliziert, dass es auf einer Hash-Tabelle basiert. Die Reihenfolge der Hash-Tabellen hängt hauptsächlich von den Hashcodes der Elemente in der Gruppe ab, nicht von der Einfügereihenfolge.
Qwertie
Zustimmen. Siehe Jon Skeets Antwort als Gegenbeispiel. Hier ist eine verwandte Frage zu einer Implementierung einer solchen HashTable - wenn Sie die Einfügereihenfolge garantiert beibehalten möchten. stackoverflow.com/questions/1552225/…
George Mamaladze
@BrianRasmussen haha ​​... lies das einfach in MSDN und navigiere hier nur für den Fall ... + 1, weil
du
beantworte die Frage!
OuuGiii
"deren Elemente in keiner bestimmten Reihenfolge sind"
Michael Burr
44

Ich denke, der Artikel, der behauptet, er bewahre die Bestellung, ist einfach falsch. Bei einfachen Tests kann die Einfügereihenfolge aufgrund der internen Struktur zwar beibehalten werden, sie ist jedoch nicht garantiert und funktioniert nicht immer so. Ich werde versuchen, ein Gegenbeispiel zu finden.

EDIT: Hier ist das Gegenbeispiel:

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);
        }
    }
}

Dies druckt 1, 4, 3, obwohl 3 vor 4 eingefügt wurde.

Es ist möglich, dass die Einfügereihenfolge beibehalten wird, wenn Sie niemals Elemente entfernen. Ich bin mir nicht sicher, aber ich wäre nicht ganz überrascht. Ich denke jedoch, dass es eine sehr schlechte Idee wäre, sich darauf zu verlassen:

  • Es ist nicht dokumentiert, dass dies so funktioniert, und in der Dokumentation wird ausdrücklich angegeben, dass es nicht sortiert ist.
  • Ich habe mir die internen Strukturen oder den Quellcode nicht angesehen (was ich natürlich nicht habe) - ich müsste sie sorgfältig studieren, bevor ich einen solchen Anspruch auf feste Weise geltend machen kann.
  • Die Implementierung kann sehr leicht zwischen den Versionen des Frameworks wechseln. Unter Berufung auf diese wäre wie unter Berufung auf der string.GetHashCodeUmsetzung nicht zu ändern - die einige Leute in den .NET 1,1 Tagen taten zurück, und dann wurde sie verbrannt , wenn die Umsetzung tut Änderung in .NET 2.0 ...
Jon Skeet
quelle
Das ist auch meine Annahme. Leider behaupten andere Artikel dasselbe (basierend auf diesem Artikel). Es wäre schön, eine endgültige Ja / Nein-Antwort von einer zuverlässigen Quelle zu haben.
Brian Rasmussen
Ich bin etwas entsetzt über die Menge an Fehlinformationen darüber trotz der offiziellen Dokumentation. Fand auch diese Seite ezinearticles.com/?C-HashSet-Advantages&id=1761474 , die auch in Google-Suchanfragen hoch war. Das Schlimmste daran ist, dass es speziell erkennt, dass es zwei verschiedene Arten von Set-Implementierungen gibt: diejenigen, die die Reihenfolge beibehalten und nicht, aber es wird ausdrücklich behauptet, dass in .NET ein HashSet die Reihenfolge beibehält.
Davy8
foreachiteriert nicht in der richtigen Reihenfolge. Verwenden Sie immer 'für' und Indizes.
Mihai Bratulescu
@MihaiBratulescu: Es iteriert in der Reihenfolge, in der die Aufrufe MoveNextzurückgegeben werden. Für jeden mir bekannten geordneten Typ entspricht dies der Reihenfolge, in der der Index verwendet wird. Beachten Sie, dass in der in Rede stehenden Art ( HashSet<T>) gibt ist kein Indexer. Können Sie ein konkretes Beispiel geben, bei dem Sie der Meinung sind, dass es besser ist, einen Index als eine foreach-Schleife zu verwenden?
Jon Skeet
7

In der Dokumentation heißt es:

Eine HashSet <(Of <(T>)>) - Sammlung ist nicht sortiert und kann keine doppelten Elemente enthalten. Wenn die Duplizierung von Ordnungen oder Elementen für Ihre Anwendung wichtiger ist als die Leistung, sollten Sie die Klasse List <(Of <(T>)>) zusammen mit der Sortiermethode verwenden.

Daher spielt es keine Rolle, ob die Reihenfolge der Elemente in der aktuellen Implementierung tatsächlich beibehalten wird, da dies nicht dokumentiert ist, und selbst wenn es jetzt so aussieht, kann sich dies zu jedem Zeitpunkt in der Zukunft ändern (selbst in einem Hotfix zu der Rahmen).

Sie sollten anhand dokumentierter Verträge programmieren , nicht anhand von Implementierungsdetails .

Greg Beech
quelle
Ich stimme zu, aber ich dachte nicht, dass das obige Zitat ausreichen würde, um die Botschaft zu vermitteln. Ich war mir ziemlich sicher, dass das Set ungeordnet sein würde, ich suchte nur nach einer klaren Dokumentation.
Brian Rasmussen
3

SortedSet<T>In .NET4 gibt es speziell eine Sammlung .

Dies würde Ihnen eine Sortierung geben, aber es ist unwahrscheinlich, dass es sich um eine Sortierreihenfolge handelt. Da Sie eine benutzerdefinierte verwenden können, können IComparerSie theoretisch alles tun.

Chris Marisic
quelle
2

Nein, ein Hash-Satz behält die Einfügereihenfolge nicht bei, zumindest nicht vorhersehbar. Sie können ein LinkedHashSet (Java) oder ein gleichwertiges verwenden. Ein LinkedHashSet behält die Ordnung bei.

Wenn Sie bestellen möchten, sollten Sie nicht einmal ein Set verwenden ... es ist nicht für bestellte Elemente gedacht, außer in Ausnahmefällen.

EDIT: klingt wie ich predige: - / Sorry.

Sudhir Jonathan
quelle
1
Ich versuche nicht, HashSet auf diese Weise zu verwenden, sondern Kollegen daran zu hindern.
Brian Rasmussen
ah, okay ... wir hatten hier das gleiche Problem bei unserem Projekt. Es war jedoch notwendig, weil wir eine geordnete Liste wollten, die voller einzigartiger Gegenstände sein musste.
Sudhir Jonathan
2

Wenn Sie den Quellcode für HashSet.AddIfNotPresent lesen, sehen Sie, dass die Einfügereihenfolge beibehalten wird, vorausgesetzt, es wurden keine Löschvorgänge durchgeführt .

So new HashSet<string> { "Tom", "Dick", "Harry" }bleibt die Ordnung erhalten, aber wenn Sie dann Dick entfernen und Rick hinzufügen, lautet die Reihenfolge ["Tom", "Rick", "Harry"].

Oberst Panik
quelle
Einverstanden. Solange Sie ein Element nie entfernen, werden sie in der Einfügereihenfolge aufgelistet. Dies kann eine nützliche Eigenschaft sein und wird niemals aus der Klasse entfernt, selbst wenn sie nicht dokumentiert ist. Das Team würde einfach nicht riskieren, Anwendungen zu beschädigen, die von diesem Verhalten abhängen.
Drew Noakes