Garantiert List <T> die Einfügereihenfolge?

238

Angenommen, ich habe 3 Zeichenfolgen in einer Liste (z. B. "1", "2", "3").

Dann möchte ich sie neu anordnen, um "2" an Position 1 zu platzieren (z. B. "2", "1", "3").

Ich verwende diesen Code (setze indexToMoveTo auf 1):

listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, itemToMove);

Dies scheint zu funktionieren, aber ich bekomme gelegentlich seltsame Ergebnisse; Manchmal ist die Reihenfolge falsch oder Artikel aus der Liste werden gelöscht!

Irgendwelche Ideen? Ist List<T>Garantie Bestellung?

Verbunden:

Garantiert eine Liste <T>, dass Artikel in der Reihenfolge zurückgegeben werden, in der sie hinzugefügt wurden?

SuperSuperDev1234
quelle
3
Dies ist nicht wahr, wie hier erwähnt: stackoverflow.com/a/1790318/696517
jrmgx

Antworten:

311

Die List<>Klasse garantiert die Reihenfolge - die Dinge bleiben in der Liste in der Reihenfolge erhalten, in der Sie sie hinzufügen, einschließlich Duplikate, es sei denn, Sie sortieren die Liste explizit.

Laut MSDN:

... Liste "Stellt eine stark typisierte Liste von Objekten dar, auf die über den Index zugegriffen werden kann ."

Die Indexwerte müssen zuverlässig bleiben, damit dies korrekt ist. Daher ist die Bestellung garantiert.

Möglicherweise erhalten Sie seltsame Ergebnisse aus Ihrem Code, wenn Sie das Element später in der Liste verschieben, da Sie Remove()alle anderen Elemente vor dem Aufruf an eine Stelle nach unten verschieben Insert().

Können Sie Ihren Code auf etwas reduzieren, das klein genug ist, um ihn zu veröffentlichen?

Bevan
quelle
63
Für alle zukünftigen Googler ist hier das genaue Zitat aus der MSDN (fettgedruckte Mine) für Liste (T). Hinzufügen Das Objekt, das am Ende der Liste <T> hinzugefügt werden soll . Der Wert kann für Referenztypen null sein.
Aolszowka
4
Gibt es ein definitiveres Zitat / eine Referenz, die wir von Microsoft oder der C # -Spezifikation dazu erhalten könnten? @ aolszowkas Zitat scheint definitiv darauf hinzudeuten, dass die Einfügereihenfolge beibehalten wird, aber technisch gesehen könnte die Liste die Sammlung jederzeit nach dem Hinzufügen eines Artikels neu ordnen, und diese Aussage wäre weiterhin gültig. Ich möchte nicht pingelig werden, aber wenn ein Manager oder eine Qualitätssicherung mich wirklich dazu gebracht hätte, diese Position zu verteidigen, würde ich mich mit diesem Zitat nicht sehr sicher fühlen.
Dorf
4
Ich kann mir zwei nützliche Möglichkeiten vorstellen, um eine Bestätigung zu erhalten. Zuerst lesen Sie die Quelle und überzeugen Sie sich selbst. Zweitens sollten Sie die Definition des abstrakten Datentyps Listin einem guten Lehrbuch der Informatik überprüfen . Genau wie Queueund Stackist a Listeine gut definierte, vorhersehbare und gut verstandene Datenstruktur - wenn sich die .NET-Implementierung unterscheidet (oder wenn sie sich ändert), würde eine Menge Software kaputt gehen.
Bevan
@tehDorf Meine Meinung dazu (falls es eine Diskussion darüber geben sollte) lautet: "Wenn die Reihenfolge den Betrieb Ihrer Methode / Ihres Algorithmus beeinflusst, sollten Sie die Liste explizit ordnen oder Ihre API die entsprechende Schnittstelle / Klasse wie IOrderedEnumerable oder verwenden lassen SortedList, andernfalls sollten Sie sich nicht auf eine bestimmte Implementierung verlassen, um sich auf eine bestimmte Weise zu verhalten, es sei denn, dies wird ausdrücklich eindeutig angegeben. "Sie müssen auch vorsichtig sein, wenn Sie List <T> explizit sortieren, da ihre Implementierung eine instabile Sortierung verwendet.
Aolszowka
1
@achuthakrishnan Sie sind separate Themen. Die Liste garantiert, dass die Elemente in der Reihenfolge beibehalten werden, in der sie der Liste hinzugefügt wurden. Wenn Sie Where()die Liste mit der LINQ- Methode filtern, verlassen Sie sich auf die Implementierung, um die Elemente in der Reihenfolge der Reihe nach zu berücksichtigen. Es kommt vor, dass dies der Fall ist (siehe referencesource.microsoft.com/System.Core/System/Linq/… ), dies ist jedoch nicht in MSDN dokumentiert. Ich würde vorschlagen, zu verwenden, emp.LastOrDefault(x => x.empDept = 'abc')da die Dokumentation von LastOrDefault()anzeigt, dass die Reihenfolge der Elemente beibehalten wird.
Bevan
36

Hier sind 4 Elemente mit ihrem Index

0  1  2  3
K  C  A  E

Sie möchten K zwischen A und E verschieben - Sie könnten an Position 3 denken. Sie müssen bei der Indizierung hier vorsichtig sein, da nach dem Entfernen alle Indizes aktualisiert werden.

Sie entfernen also zuerst Element 0 und verlassen es

0  1  2
C  A  E

Dann fügen Sie bei 3 ein

0  1  2  3
C  A  E  K

Um das richtige Ergebnis zu erhalten, sollten Sie Index 2 verwendet haben. Um die Konsistenz zu gewährleisten, müssen Sie an (indexToMoveTo-1) senden if indexToMoveTo > indexToMove, z

bool moveUp = (listInstance.IndexOf(itemToMoveTo) > indexToMove);
listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, moveUp ? (itemToMoveTo - 1) : itemToMoveTo);

Dies kann mit Ihrem Problem zusammenhängen. Beachten Sie, dass mein Code nicht getestet wurde!

BEARBEITEN : Alternativ können Sie auch Sorteinen benutzerdefinierten Vergleicher ( IComparer) verwenden, wenn dies für Ihre Situation gilt.

Joel Goodwin
quelle
Ich habe Bevans Antwort nicht sorgfältig genug gelesen, ich habe gerade den Boden abgedeckt, den er hat.
Joel Goodwin
9
Ja, aber Sie haben ein Beispiel für Bevans Antwort sowie einen Lösungscode ausgearbeitet und angegeben, sodass Ihre Antwort nicht tautolog ist und ich Sie gewählt habe.
Jason S
9

Wie Bevan sagte, aber denken Sie daran, dass der Listenindex auf 0 basiert. Wenn Sie ein Element an den Anfang der Liste verschieben möchten, müssen Sie es am Index 0 einfügen (nicht 1, wie in Ihrem Beispiel gezeigt).

M4N
quelle
1

Dies ist der Code, den ich habe, um ein Element an einer Stelle in einer Liste nach unten zu verschieben:

if (this.folderImages.SelectedIndex > -1 && this.folderImages.SelectedIndex < this.folderImages.Items.Count - 1)
{
    string imageName = this.folderImages.SelectedItem as string;
    int index = this.folderImages.SelectedIndex;

    this.folderImages.Items.RemoveAt(index);
    this.folderImages.Items.Insert(index + 1, imageName);
    this.folderImages.SelectedIndex = index + 1;
 }

und dies, um es um einen Platz nach oben zu bewegen:

if (this.folderImages.SelectedIndex > 0)
{
    string imageName = this.folderImages.SelectedItem as string;
    int index = this.folderImages.SelectedIndex;

    this.folderImages.Items.RemoveAt(index);
    this.folderImages.Items.Insert(index - 1, imageName);
    this.folderImages.SelectedIndex = index - 1;
}

folderImagesist a ListBoxnatürlich, also ist die Liste a ListBox.ObjectCollection, nicht a List<T>, aber sie erbt von, IListalso sollte sie sich gleich verhalten. Hilft das?

Ersteres funktioniert natürlich nur, wenn das ausgewählte Element nicht das letzte Element in der Liste ist, und letzteres, wenn das ausgewählte Element nicht das erste Element ist.

ChrisF
quelle
1

Wenn Sie die Reihenfolge der Operationen ändern, vermeiden Sie das seltsame Verhalten: Fügen Sie zuerst den Wert an der richtigen Stelle in die Liste ein und löschen Sie ihn dann an seiner ersten Position. Stellen Sie sicher, dass Sie es durch seinen Index löschen, denn wenn Sie es als Referenz löschen, können Sie beide löschen ...

Asaf
quelle