Яка різниця між HashSet <T> та Список <T>?


156

Можете чи ви пояснити , в чому різниця між HashSet<T>і List<T>в .NET?

Може, ви можете пояснити на прикладі, у яких випадках HashSet<T>слід віддати перевагу List<T>?



Я пропоную вам ознайомитися зі статтями Вікіпедії на en.wikipedia.org/wiki/Hash_table та en.wikipedia.org/wiki/Dynamic_array .
mqp

Інформацію про те, що стосується продуктивності, дивіться у виконанні хешсет-проти-списку
nawfal

Відповіді:


213

На відміну від списку <> ...

  1. HashSet - це список без повторюваних членів.

  2. Оскільки HashSet обмежений вмістом лише унікальних записів, внутрішня структура оптимізована для пошуку (порівняно зі списком) - це значно швидше

  3. Додавання до HashSet повертає логічне значення - false, якщо додавання не вдалося через вже наявне в Set

  4. Може виконувати операції з математичним набором проти набору: Union / Intersection / IsSubsetOf і т.д.

  5. HashSet не реалізує IList лише ICollection

  6. Не можна використовувати індекси з HashSet, лише з перелічниками.

Основною причиною використання HashSet буде, якщо ви зацікавлені у виконанні операцій Set.

Дано 2 набори: hashSet1 і hashSet2

 //returns a list of distinct items in both sets
 HashSet set3 = set1.Union( set2 );

летить порівняно з еквівалентною операцією за допомогою LINQ. Це також акуратніше писати!


IDK, у мене були проблеми з Unionметодом. Я використовував UnionWithзамість цього.
користувач

2
+1 для "Основна причина використання HashedSet буде, якщо ви зацікавлені в виконанні операцій із встановленням."
LCJ

12
Насправді я віддаю перевагу відповіді, яка вказує на те, що HashSets підходять у випадках, коли ви можете ставитися до своєї колекції як до «сумкових предметів». Налаштування операцій не так часто, як перевірка утримання. У будь-який момент, у вас є набір унікальних елементів (наприклад, кодів), і вам потрібно перевірити, чи не міститься, HashSet зручний.
ThunderGr

Хороша відповідь. Я також спокусився додати декілька характерних відмінностей продуктивності.
nawfal

1
Питання: головна причина - не впевненість у тому, щоб не було дублікатів?
Андреа Скарафоні

54

Якщо точніше, давайте продемонструємо на прикладах,

Ви не можете використовувати HashSet, як у наступному прикладі.

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
for (int i = 0; i < hashSet1.Count; i++)
    Console.WriteLine(hashSet1[i]);

hashSet1[i] призведе до помилки:

Неможливо застосувати індексацію за допомогою [] до виразу типу "System.Collections.Generic.HashSet"

Ви можете використовувати оператор foreach:

foreach (var item in hashSet1)
    Console.WriteLine(item);

Ви не можете додати дублюючі елементи до HashSet, тоді як Список дозволяє це зробити, і поки ви додаєте елемент до HashSet, ви можете перевірити, чи він містить цей предмет чи ні.

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
if (hashSet1.Add("1"))
   Console.WriteLine("'1' is successfully added to hashSet1!");
else
   Console.WriteLine("'1' could not be added to hashSet1, because it contains '1'");

HashSet має деякі корисні функції , такі як IntersectWith, UnionWith, IsProperSubsetOf, ExceptWith, і SymmetricExceptWithт.д.

IsProperSubsetOf:

HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
HashSet<string> hashSet3 = new HashSet<string>() { "1", "2", "3", "4", "5" };
if (hashSet1.IsProperSubsetOf(hashSet3))
    Console.WriteLine("hashSet3 contains all elements of hashSet1.");
if (!hashSet1.IsProperSubsetOf(hashSet2))
    Console.WriteLine("hashSet2 does not contains all elements of hashSet1.");

UnionWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
hashSet1.UnionWith(hashSet2); //hashSet1 -> 3, 2, 4, 6, 8

IntersectWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" }
hashSet1.IntersectWith(hashSet2);//hashSet1 -> 4, 8

ExceptWith :

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.ExceptWith(hashSet2);//hashSet1 -> 5, 6

SymmetricExceptWith :

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.SymmetricExceptWith(hashSet2);//hashSet1 -> 4, 5, 6

До речі, порядок не зберігається в HashSets. У прикладі ми додали останній елемент "2", але це у другому порядку:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
hashSet1.Add("1");    // 3, 4, 8, 1
hashSet1.Remove("4"); // 3, 8, 1
hashSet1.Add("2");    // 3, 2 ,8, 1

51

A HashSet<T>- клас, призначений для того, щоб дати вам O(1)пошук вмісту (тобто чи містить ця колекція конкретний об'єкт, і швидко скажіть мені відповідь).

A List<T>- клас, призначений для отримання колекції з O(1)випадковим доступом, ніж може динамічно зростати (подумайте про динамічний масив). Ви можете перевірити обмеження в O(n)часі (якщо список не відсортований, ви можете O(log n)вчасно виконати двійковий пошук ).

Можливо, ви можете пояснити на прикладі, у яких випадках HashSet<T>слід віддати перевагуList<T>

Коли ви хочете перевірити вміст в O(1).


За винятком O (log n), якщо список відсортований; Зрештою, це швидше, ніж пошук у несортованому списку.
Андрій

20

Використовуйте, List<T>коли ви хочете:

  • Зберігайте колекцію предметів у певному порядку.

Якщо ви знаєте, індекс потрібного товару (а не значення самого елемента) - це пошук O(1). Якщо ви не знаєте індексу, для пошуку O(n)несортованої колекції потрібен більше часу .

Використовуйте, Hashset<T>коли ви хочете:

  • Швидко з’ясуйте, чи міститься певний об’єкт у колекції.

Якщо ви знаєте назву речі, яку ви хочете знайти, Lookup є O(1)(це частина 'Hash'). Він не підтримує впорядкування, як List<T>це, і ви не можете зберігати дублікати (додавання дубліката не має ефекту, це частина "Встановити").

Прикладом того, як використовувати a, Hashset<T>було б, якщо ви хочете дізнатися, чи слово, яке грається в гру Scrabble, є дійсним словом англійською мовою (чи іншою мовою). Ще краще, якби ви хотіли створити веб-сервіс, який би використовувався всіма прикладами онлайн-версії такої гри.

Було List<T>б гарною структурою даних для створення табло для відстеження результатів гравців.


15

Список - упорядкований список. це є

  • доступ до якого здійснюється за допомогою цілого індексу
  • може містити дублікати
  • має передбачуваний порядок

HashSet - це набір. Це:

  • Можна заблокувати повторювані елементи (див. Додати (T) )
  • Не гарантує замовлення предметів у межах набору
  • Є операції, які ви очікували б на наборі, наприклад , IntersectWith, IsProperSubsetOf, UnionWith.

Список більш доцільний, коли ви хочете отримати доступ до своєї колекції так, ніби це був масив, до якого ви могли додавати, вставляти та видаляти елементи. HashSet - кращий вибір, якщо ви хочете ставитися до своєї колекції як до «мішка» з предметами, в якому порядок не важливий, або коли ви хочете порівнювати його з іншими наборами, використовуючи такі операції, як IntersectWith або UnionWith.



3

Список - це упорядкована колекція об'єктів типу T, які на відміну від масиву можна додавати та видаляти записи.

Ви б використовували список, де ви хочете посилатися на членів у порядку, коли ви їх зберігали, і ви отримуєте доступ до них за позицією, а не самим елементом.

HashSet - це як словник, що сам елемент є ключовим, а також значенням, впорядкованість не гарантується.

Ви б використовували HashSet там, де ви хочете перевірити, чи є об’єкт у колекції


1
Для уточнення, якщо хто-небудь ще прочитає це неправильне на перший погляд - Listпідтримує замовлення (тобто коли речі були додані), але не сортує автоматично елементи. Вам доведеться зателефонувати .Sortабо скористатися SortedList.
drzaus

1

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

Крім того, якщо використовується клас DataAnnotations, можна реалізувати логіку Key щодо властивостей класу та ефективно керувати природним індексом (кластеризованим чи ні) за допомогою HashSet, де це буде дуже складно в реалізації списку.

Важливим варіантом використання списку є реалізація загальної інформації для кількох носіїв у моделі перегляду, наприклад, пересилання списку класів у MVC View для помічника DropDownList, а також для надсилання у вигляді конструкції JSON через WebApi. Список дозволяє використовувати типову логіку колекціонування класів і зберігає гнучкість для більш «інтерфейсного» підходу до обчислення однієї моделі перегляду для різних середовищ.

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