Чи гарантує Список <T> порядок вставки?


238

Скажіть, у мене в списку є 3 рядки (наприклад, "1", "2", "3").

Потім я хочу змінити їх порядок для розміщення "2" у позиції 1 (наприклад, "2", "1", "3").

Я використовую цей код (встановлення indexToMoveTo до 1):

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

Це, здається, працює, але я час від часу отримую дивні результати; іноді замовлення неправильне або елементи зі списку видаляються!

Будь-які ідеї? Чи List<T>гарантує замовлення?

Пов'язані:

Чи гарантує Список <T>, що елементи будуть повернуті в тому порядку, в якому вони були додані?


3
Це не так , як вказано тут: stackoverflow.com/a/1790318/696517
jrmgx

Відповіді:


311

List<>Клас робить гарантійний замовлення - речі будуть збережені в списку в порядку їх додавання, включаючи дублікати, якщо ви явно сортування списку.

За даними MSDN:

... Список "Представляє сильно набраний список об'єктів, до яких можна отримати доступ за індексом ."

Значення індексу повинні залишатися надійними, щоб це було точним. Тому порядок гарантований.

Можливо, ви отримаєте незвичайні результати від свого коду, якщо ви переміщуватимете предмет пізніше у списку, оскільки ви Remove()перемістите всі інші елементи вниз на одне місце до дзвінка Insert().

Чи можете ви звести свій код до чогось досить маленького для публікації?


63
Для будь-якого майбутнього googler's ось точна цитата з MSDN (жирний шрифт) для List (T) .Add Об'єкт, який потрібно додати до кінця списку <T>. Для еталонних типів значення може бути нульовим.
aolszowka

4
Чи є більш чітка цитата / довідка, яку ми могли б отримати від Microsoft або специфікації C # щодо цього? Цитата @ aolszowka, безумовно, говорить про те, що вона зберігає порядок вставки, але технічно Список може повторно замовити колекцію в будь-який час після додавання елемента, і це твердження все ще буде дійсним. Я не хочу ставитись до цього, але якщо мене керівник чи QA дійсно змусили відстоювати цю позицію, я б не почувався дуже впевнено саме цією цитатою.
tehDorf

4
Я можу придумати два корисні способи отримати певне підтвердження. По-перше, прочитайте джерело та задовольнить себе. По-друге, ознайомтеся з визначенням абстрактного типу даних Listу будь-якому хорошому підручнику з інформатики. Так само як Queueі Stack, a List- це чітко визначена, передбачувана і добре зрозуміла структура даних - якщо реалізація .NET відрізнялася (або якщо вона змінювалася), багато програмного забезпечення було б зламано.
Беван

@tehDorf Моє прийняття до цього питання (якщо б на ньому було обговорення) таке: "Якщо замовлення впливає на роботу вашого методу / алгоритму, ви повинні чітко замовити список або ваш API взяти відповідний інтерфейс / клас, наприклад IOrderedEnumerable або SortedList, інакше не слід покладатися на певну реалізацію, щоб вона вела себе певним чином, якщо це прямо не визначено однозначно "Ви також повинні бути обережними щодо явного сортування списку <T>, оскільки для їх реалізації використовується нестабільний сортування.
aolszowka

1
@achuthakrishnan Це окремі питання. Список гарантує, що елементи зберігаються у порядку, коли вони додані до списку. Коли ви використовуєте Where()метод LINQ для фільтрування списку, ви покладаєтесь на реалізацію, щоб розглянути елементи в послідовності в порядку. Буває, що так і є (див. Referenceource.microsoft.com/System.Core/System/Linq/… ), але це не зафіксовано в MSDN. Я б запропонував використовувати emp.LastOrDefault(x => x.empDept = 'abc')в документації дій LastOrDefault()вказівку, що вона підтримує порядок позицій.
Беван

36

Ось 4 позиції з їх індексом

0  1  2  3
K  C  A  E

Ви хочете перемістити K на між A і E - ви можете подумати, що позиція 3. Ви повинні бути обережними щодо індексації тут, оскільки після видалення всі індекси оновлюються.

Отже, ви видалите спочатку пункт 0, залишивши

0  1  2
C  A  E

Потім ви вставляєте на 3

0  1  2  3
C  A  E  K

Щоб отримати правильний результат, вам слід було б скористатися індексом 2. Для того, щоб все було послідовно, вам потрібно буде надіслати (indexToMoveTo-1) if indexToMoveTo > indexToMove, наприклад

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

Це може бути пов’язано з вашою проблемою. Зверніть увагу, мій код не перевірений!

EDIT : Крім того, ви можете Sortскористатись спеціальним порівняльником ( IComparer), якщо це стосується вашої ситуації.


Я не прочитав відповіді Бевана досить уважно, я щойно накрив його землею.
Джоель Гудвін

9
Так, але ви розробили і навели приклад відповіді Бевана, а також якийсь код рішення, тому ваша відповідь не є тавтологічною, і я проголосував за вас.
Jason S

9

Як сказав Беван, але майте на увазі, що список-індекс базується на 0. Якщо ви хочете перемістити елемент на передню частину списку, вам слід вставити його в індекс 0 (а не 1, як показано у вашому прикладі).


1

Це код, який я маю для переміщення елемента вниз в одному списку:

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

і це для переміщення його на одне місце вгору:

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

folderImagesце ListBox, звичайно , так що список є ListBox.ObjectCollection, а НЕ List<T>, але він успадковує від IListтак він повинен вести себе так само. Чи допомагає це?

Звичайно, перший працює лише тоді, коли вибраний елемент не є останнім у списку, а останній, якщо вибраний елемент не є першим.


1

Якщо ви зміните порядок операцій, ви уникнете дивної поведінки: Спочатку вставте значення в потрібне місце в списку, а потім видаліть його з його першої позиції. Переконайтесь, що ви видалите його за індексом, адже якщо ви видалите його за посиланням, ви можете видалити їх обоє ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.