List <T> garantit-il l'ordre d'insertion?

238

Disons que j'ai 3 chaînes dans une liste (par exemple "1", "2", "3").

Ensuite, je veux les réorganiser pour placer "2" en position 1 (par exemple "2", "1", "3").

J'utilise ce code (en définissant indexToMoveTo sur 1):

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

Cela semble fonctionner, mais j'obtiens parfois des résultats étranges; Parfois, la commande est incorrecte ou des éléments de la liste sont supprimés!

Des idées? Est-ce List<T>que la commande est garantie?

En relation:

Une liste <T> garantit-elle que les articles seront retournés dans l'ordre dans lequel ils ont été ajoutés?

SuperSuperDev1234
la source
3
Ce n'est pas vrai comme mentionné ici: stackoverflow.com/a/1790318/696517
jrmgx

Réponses:

311

La List<>classe garantit la commande - les éléments seront conservés dans la liste dans l'ordre dans lequel vous les ajoutez, y compris les doublons, sauf si vous triez explicitement la liste.

Selon MSDN:

... List "Représente une liste fortement typée d'objets accessibles par index ."

Les valeurs d'index doivent rester fiables pour que cela soit précis. Par conséquent, la commande est garantie.

Vous pourriez obtenir des résultats étranges de votre code si vous déplacez l'élément plus tard dans la liste, car vous Remove()déplacerez tous les autres éléments d'un endroit avant l'appel à Insert().

Pouvez-vous réduire votre code à quelque chose d'assez petit pour le publier?

Bevan
la source
63
Pour tout futur googleur, voici la citation exacte du MSDN (mine en gras) pour List (T) .Add L'objet à ajouter à la fin de la liste <T>. La valeur peut être nulle pour les types de référence.
aolszowka
4
Y a-t-il une citation / référence plus définitive que nous pourrions obtenir de Microsoft ou de la spécification C # à ce sujet? La citation de @ aolszowka semble définitivement suggérer qu'elle conserve l'ordre d'insertion, mais techniquement, la liste pourrait réorganiser la collection à tout moment après l'ajout d'un élément et cette déclaration serait toujours valide. Je ne veux pas être pointilleux à ce sujet, mais si un manager ou un QA m'a vraiment fait défendre cette position, je ne me sentirais pas très confiant avec cette citation.
tehDorf
4
Je peux penser à deux façons utiles d'obtenir une confirmation. Tout d'abord, lisez la source et assurez-vous. Deuxièmement, consultez la définition du type de données abstrait Listdans tout bon manuel d'informatique. Tout comme Queueet Stack, a Listest une structure de données bien définie, prévisible et bien comprise - si l'implémentation .NET différait (ou si elle changeait), beaucoup de logiciels se briseraient.
Bevan
@tehDorf Mon point de vue (s'il devait y avoir une discussion à ce sujet) est: "Si l'ordre affecte le fonctionnement de votre méthode / algorithme, vous devez explicitement commander la liste ou demander à votre API de prendre l'interface / classe appropriée telle que IOrderedEnumerable ou SortedList, sinon vous ne devez pas compter sur une implémentation particulière pour se comporter d'une certaine manière, sauf si cela est explicitement indiqué sans ambiguïté. "Vous devez également faire attention au tri explicite de List <T> car leur implémentation utilise un tri instable.
aolszowka du
1
@achuthakrishnan Ce sont des questions distinctes. La liste garantit que les éléments sont conservés dans l'ordre où ils sont ajoutés à la liste. Lorsque vous utilisez la Where()méthode LINQ pour filtrer la liste, vous comptez sur l'implémentation pour considérer les éléments de la séquence dans l'ordre. Il arrive que ce soit le cas (voir referencesource.microsoft.com/System.Core/System/Linq/… ), mais cela n'est pas documenté dans MSDN. Je suggère d'utiliser emp.LastOrDefault(x => x.empDept = 'abc')car la documentation LastOrDefault()indique qu'elle maintient l'ordre des articles.
Bevan
36

Voici 4 articles, avec leur index

0  1  2  3
K  C  A  E

Vous voulez déplacer K entre A et E - vous pourriez penser à la position 3. Vous devez faire attention à votre indexation ici, car après la suppression, tous les index sont mis à jour.

Vous supprimez donc l'élément 0 en premier, laissant

0  1  2
C  A  E

Ensuite, vous insérez à 3

0  1  2  3
C  A  E  K

Pour obtenir le résultat correct, vous devriez avoir utilisé l'index 2. Pour rendre les choses cohérentes, vous devrez envoyer à (indexToMoveTo-1) if indexToMoveTo > indexToMove, par exemple

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

Cela peut être lié à votre problème. Notez que mon code n'a pas été testé!

EDIT : Alternativement, vous pourriez Sortavec un comparateur personnalisé ( IComparer) si cela s'applique à votre situation.

Joel Goodwin
la source
Je n'ai pas lu la réponse de Bevan assez attentivement, je viens de couvrir le terrain qu'il a.
Joel Goodwin
9
Oui, mais vous avez élaboré et donné un exemple de la réponse de Bevan, ainsi qu'un code de solution, donc votre réponse n'est pas tautologue et je vous ai voté.
Jason S
9

Comme l'a dit Bevan, mais gardez à l'esprit que l'index de liste est basé sur 0. Si vous souhaitez déplacer un élément au début de la liste, vous devez l'insérer à l'index 0 (pas 1 comme indiqué dans votre exemple).

M4N
la source
1

Voici le code que j'ai pour déplacer un élément vers le bas d'un endroit dans une liste:

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

et ceci pour le déplacer d'une place vers le haut:

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

folderImagesest ListBoxbien sûr donc la liste est un ListBox.ObjectCollection, pas un List<T>, mais il hérite IListdonc il devrait se comporter de la même manière. est-ce que cela aide?

Bien sûr, le premier ne fonctionne que si l'élément sélectionné n'est pas le dernier élément de la liste et le dernier si l'élément sélectionné n'est pas le premier élément.

ChrisF
la source
1

Si vous changez l'ordre des opérations, vous éviterez le comportement étrange: insérez d'abord la valeur au bon endroit dans la liste, puis supprimez-la de sa première position. Assurez-vous de le supprimer par son index, car si vous le supprimez par référence, vous pouvez les supprimer tous les deux ...

Asaf
la source