В чем разница между HashSet <T> и List <T>?

156

Можете ли вы объяснить, в чем разница между HashSet<T>и List<T>в .NET?

Может быть, вы можете объяснить на примере, в каких случаях HashSet<T>следует отдавать предпочтение List<T>?

pencilCake
источник
22
Некоторое чтение по теме: Основы C # / .NET: Выбор Правильного Класса Коллекции
Фредрик Мёрк
Я предлагаю вам ознакомиться со статьями Википедии на en.wikipedia.org/wiki/Hash_table и en.wikipedia.org/wiki/Dynamic_array .
Mqp

Ответы:

213

В отличие от списка <> ...

  1. HashSet - это список без повторяющихся членов.

  2. Поскольку HashSet ограничен, чтобы содержать только уникальные записи, внутренняя структура оптимизирована для поиска (по сравнению со списком) - это значительно быстрее

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

  4. Может выполнять математические операции над множествами: объединение / пересечение / 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. Это также аккуратнее писать!

BonyT
источник
ИДК, у меня были проблемы с Unionметодом. Я использовал UnionWithвместо этого.
пользователь
2
+1 за «Основную причину использования HashedSet было бы, если вы заинтересованы в выполнении операций Set».
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, пока List позволяет вам это делать, и когда вы добавляете элемент в 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
GorkemHalulu
источник
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, допустимым словом на английском (или другом языке). Еще лучше было бы, если бы вы хотели создать веб-сервис, который будет использоваться всеми экземплярами онлайн-версии такой игры.

A List<T>была бы хорошей структурой данных для создания табло для отслеживания результатов игрока.

Waylon Flinn
источник
15

Список - это упорядоченный список. это

  • доступ по целочисленному индексу
  • может содержать дубликаты
  • имеет предсказуемый порядок

HashSet - это набор. Это:

  • Может блокировать повторяющиеся элементы (см. Добавить (T) )
  • Не гарантирует порядок товаров в наборе
  • Имеет операции, которые вы ожидаете на множестве, например , IntersectWith, IsProperSubsetOf, UnionWith.

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

dgvid
источник
3

Список не обязательно уникален, в то время как hashset для одного.

therealmitchconnors
источник
3

Список - это упорядоченная коллекция объектов типа T, в отличие от массива, вы можете добавлять и удалять записи.

Вы бы использовали список, в котором вы хотите ссылаться на участников в том порядке, в котором вы их сохранили, и вы получаете к ним доступ по позиции, а не по самому элементу.

HashSet похож на словарь, в котором сам элемент является ключом и значением, порядок не гарантируется.

Вы бы использовали HashSet, где вы хотите проверить, что объект находится в коллекции

Боб Вейл
источник
1
Чтобы уточнить, в случае, если кто-то еще прочитает, что это неправильно на первый взгляд - Listподдерживает порядок (то есть, когда вещи были добавлены), но не сортирует элементы автоматически. Вам придется позвонить .Sortили использовать SortedList.
drzaus
1

Если вы решите применить эти структуры данных для фактического использования в разработке, управляемой данными, HashSet ОЧЕНЬ будет полезен при тестировании репликации на источники адаптеров данных, для очистки и переноса данных.

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

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

Натан Тиг
источник