Czy lista <T> gwarantuje zamówienie na wstawienie?

238

Powiedzmy, że mam 3 ciągi na liście (np. „1”, „2”, „3”).

Następnie chcę zmienić ich kolejność, aby umieścić „2” w pozycji 1 (np. „2”, „1”, „3”).

Korzystam z tego kodu (ustawiam indexToMoveTo na 1):

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

To wydaje się działać, ale czasami uzyskuję dziwne wyniki; czasami zamówienie jest nieprawidłowe lub elementy z listy są usuwane!

Jakieś pomysły? Czy List<T>zamówienie gwarancyjne?

Związane z:

Czy lista <T> gwarantuje, że przedmioty zostaną zwrócone w kolejności, w jakiej zostały dodane?

SuperSuperDev1234
źródło
3
To nie jest prawda, jak wspomniano tutaj: stackoverflow.com/a/1790318/696517
jrmgx

Odpowiedzi:

311

List<>Klasa nie gwarantuje uporządkowanie - rzeczy zostaną zachowane na liście w kolejności ich dodawania, włączając duplikatów, chyba że jawnie sortowania listy.

Według MSDN:

... Lista „Reprezentuje silnie wpisaną listę obiektów, do których można uzyskać dostęp za pomocą indeksu ”.

Wartości indeksu muszą pozostać wiarygodne, aby były dokładne. Dlatego zamówienie jest gwarantowane.

Możesz uzyskać dziwne wyniki z kodu, jeśli przenosisz element później na liście, ponieważ Remove()przeniesiesz wszystkie inne elementy w dół o jedno miejsce przed wezwaniem do Insert().

Czy potrafisz sprowadzić swój kod do czegoś wystarczająco małego, aby go opublikować?

Bevan
źródło
63
Dla każdego przyszłego googlera tutaj jest dokładny cytat z MSDN (pogrubiona kopalnia) dla Listy (T). Dodaj obiekt, który zostanie dodany na końcu Listy <T>. Wartość może mieć wartość NULL dla typów referencyjnych.
aolszówka
4
Czy istnieje bardziej konkretny cytat / odniesienie, które moglibyśmy uzyskać od Microsoft lub specyfikacji C # na ten temat? Cytat @ aolszówka zdecydowanie wydaje się sugerować, że zachowuje porządek wstawiania, ale technicznie Lista mogłaby ponownie zamówić kolekcję w dowolnym momencie po dodaniu elementu i to stwierdzenie nadal byłoby ważne. Nie chcę się tym denerwować, ale jeśli menedżer lub QA naprawdę zmusiłbym mnie do obrony tej pozycji, nie czułbym się bardzo pewnie z tym cytatem.
tehDorf,
4
Mogę wymyślić dwa przydatne sposoby uzyskania potwierdzenia. Najpierw przeczytaj źródło i zadowalaj się. Po drugie, sprawdź definicję abstrakcyjnego typu danych Listw dowolnym dobrym podręczniku informatyki. Podobnie jak Queuei Stack, a Listjest dobrze zdefiniowaną, przewidywalną i dobrze zrozumiałą strukturą danych - jeśli implementacja .NET będzie się różnić (lub jeśli się zmieni), wiele programów ulegnie awarii.
Bevan,
@tehDorf Moje zdanie na ten temat (jeśli miałaby być na ten temat dyskusja) brzmi: „Jeśli kolejność wpływa na działanie twojej metody / algorytmu, powinieneś jawnie zamówić listę lub poprosić, aby Twój interfejs API przyjął odpowiedni interfejs / klasę, taką jak IOrDEREnumerable lub SortedList, w przeciwnym razie nie powinieneś polegać na konkretnej implementacji, aby zachowywać się w określony sposób, chyba że zostanie to jednoznacznie określone „Musisz także być ostrożnym przy jawnym sortowaniu Listy <T>, ponieważ ich implementacja używa sortowania niestabilnego.
aolszówka
1
@achuthakrishnan Są to osobne problemy. Lista gwarantuje, że elementy zostaną zachowane w kolejności, w jakiej zostały dodane do listy. Kiedy używasz Where()metody LINQ do filtrowania listy, polegasz na implementacji, aby rozważyć kolejno pozycje w sekwencji. Zdarza się, że tak (zob. Referenceource.microsoft.com/System.Core/System/Linq/... ), ale nie jest to udokumentowane w MSDN. Sugerowałbym użycie, emp.LastOrDefault(x => x.empDept = 'abc')ponieważ dokumentacja LastOrDefault()wskazuje, że zachowuje kolejność elementów.
Bevan
36

Oto 4 elementy z ich indeksem

0  1  2  3
K  C  A  E

Chcesz przenieść K pomiędzy A i E - możesz pomyśleć o pozycji 3. Uważaj tutaj na swoje indeksowanie, ponieważ po usunięciu wszystkie indeksy są aktualizowane.

Więc najpierw usuwasz element 0, pozostawiając

0  1  2
C  A  E

Następnie wstawiasz o 3

0  1  2  3
C  A  E  K

Aby uzyskać poprawny wynik, powinieneś użyć indeksu 2. Aby zachować spójność, musisz wysłać do (indexToMoveTo-1) if indexToMoveTo > indexToMove, np.

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

Może to być związane z twoim problemem. Uwaga: mój kod nie został przetestowany!

EDYCJA : Alternatywnie możesz Sortużyć niestandardowego modułu porównującego ( IComparer), jeśli dotyczy to twojej sytuacji.

Joel Goodwin
źródło
Nie przeczytałem wystarczająco uważnie odpowiedzi Bevana, właśnie opisałem grunt, który ma.
Joel Goodwin,
9
Tak, ale opracowałeś i podałeś przykład odpowiedzi Bevana, a także kod rozwiązania, więc twoja odpowiedź nie jest tautologiczna i głosowałem za tobą.
Jason S
9

Jak powiedział Bevan, pamiętaj jednak, że indeks listy jest oparty na 0. Jeśli chcesz przenieść element na początek listy, musisz wstawić go pod indeksem 0 (nie 1, jak pokazano w twoim przykładzie).

M4N
źródło
1

Oto kod do przesunięcia elementu o jedno miejsce w dół na liście:

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

i to za przeniesienie go o jedno miejsce w górę:

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

folderImagesjest ListBoxoczywiście, więc lista ListBox.ObjectCollectionnie jest List<T>, ale dziedziczy po IListniej, więc powinna zachowywać się tak samo. czy to pomaga?

Oczywiście ten pierwszy działa tylko wtedy, gdy wybrany element nie jest ostatnim elementem na liście, a drugi, jeśli wybrany element nie jest pierwszym elementem.

ChrisF
źródło
1

Jeśli zmienisz kolejność operacji, unikniesz dziwnego zachowania: Najpierw wstaw wartość we właściwe miejsce na liście, a następnie usuń ją z jego pierwszej pozycji. Pamiętaj, aby usunąć go według jego indeksu, ponieważ jeśli usuniesz go przez odniesienie, możesz usunąć je oba ...

Asaf
źródło