C # Установить коллекцию?

392

Кто-нибудь знает, есть ли хороший эквивалент коллекции Java Set в С#? Я знаю, что вы можете несколько имитировать набор, используя Dictionary или HashTable, заполняя, но игнорируя значения, но это не очень изящный способ.

  • 0
    Вы можете найти некоторую основную информацию о Hashset здесь. dotnetk.com/c-hashset-csharp
Теги:
set
collections

9 ответов

51
Лучший ответ

Попробуйте HashSet:

Класс HashSet (Of T) обеспечивает высокопроизводительные операции набора. Набор представляет собой набор, который не содержит повторяющихся элементов и элементы которого не имеют особого порядка...

Емкость объекта HashSet (Of T) - это количество элементов, которые может удерживать объект. Объем объекта HashSet (Of T) автоматически увеличивается по мере добавления элементов к объекту.

Класс HashSet (Of T) основан на модели математических наборов и обеспечивает высокопроизводительные операции набора, аналогичные доступу к клавишам Dictionary (Of TKey, TValue) или Hashtable. Проще говоря, класс HashSet (Of T) можно рассматривать как словарь (Of TKey, TValue) без значений.

Коллекция HashSet (Of T) не сортируется и не может содержать повторяющиеся элементы...

  • 6
    К сожалению, HashSets не были добавлены до недавнего времени. Если вы работаете в более старой версии фреймворка, вам придется придерживаться своего словаря <> или Hashtable.
404

Если вы используете .NET 3.5, вы можете использовать HashSet<T>. Это правда, что .NET не обслуживает множество, а также Java.

Wintellect PowerCollections также может помочь.

  • 2
    Кто-нибудь знает, почему он называется HashSet, а не просто Set?
  • 16
    Я подозреваю, что Set является ключевым словом в некоторых языках, что может вызвать проблемы.
Показать ещё 7 комментариев
91

Структура данных HashSet<T>:

Структура структуры библиотеки классов HashSet<T> была внедрена в .NET Framework 3.5. Полный список его членов можно найти на странице MSDN для HashSet<T>.

HashSet<T> более или менее смоделирован после математический набор, что означает, что:

  • Он не может содержать повторяющихся значений.

  • Его элементы не имеют особого порядка; поэтому тип не реализует интерфейс IList<T>, но более простой ICollection<T>. Как следствие, элементы внутри хэш-набора не могут быть случайно доступны через индексы; они могут быть перепрограммированы только через счетчик.

  • Доступны определенные функции набора, такие как Union, Intersection, IsSubsetOf, IsSupersetOf. Они могут пригодиться при работе с несколькими наборами.

Другим различием между HashSet<T> и List<T> является то, что вызов метода хеш-набора Add(item) возвращает логическое значение: true, если элемент был добавлен, и false в противном случае (поскольку он уже был найден в установлен).

Почему бы не List<T>?

Так как a HashSet<T> - это просто набор уникальных объектов, вы можете задаться вопросом, почему это должна быть структура данных. Обычный List<T> может иметь такое же поведение, проверяя, найден ли объект в списке, прежде чем добавлять его.

Короткий ответ - скорость. Поиск по нормальному List<T> очень быстро происходит очень быстро, так как добавляется больше элементов. A HashSet<T> требует конструкции структуры, которая будет обеспечивать быструю скорость поиска и вставки.

Ориентиры:

Сравним скорость работы HashSet<T> и List<T>.

Каждое исследование состояло из добавления целых чисел от 0 до 9999 в каждую коллекцию. Однако mod 25 применялся к каждому целому числу. Мод 25 делает максимальные типы элементов 25. Поскольку было добавлено 10 000 элементов, это вызвало 400 столкновений, что дало структурам данных возможность использовать их алгоритмы поиска. Время измерялось 3 раза после 10 000 испытаний и усреднялось.

Не уделяйте слишком много внимания конкретному времени работы тестов, поскольку они зависят от моего оборудования, но посмотрите, как они сравниваются друг с другом.

           Average time [ms]
----------------------------
HashSet<T>             2,290
List<T>                5,505

Теперь давайте создадим объекты вместо примитивных типов. Я написал быстрый Person класс с тремя полями: Name, LastName и ID. Поскольку я не использовал какой-либо конкретный способ сравнения объектов, все элементы будут добавлены без коллизий. На этот раз 1000 экземпляров Person были добавлены в каждую коллекцию для одного испытания. Общее время 3 комплектов 1000 испытаний было усреднено.

           Average time [ms]
----------------------------
HashSet<Person>          201
List<Person>           3,000

Как вы можете видеть, разница в времени работы становится астрономической при использовании объектов, что делает HashSet<T> выгодным.

  • 13
    Разве не было бы 9975 столкновений вместо 400?
  • 1
    Вот так мы пишем отличные исчерпывающие ответы !!
14

Я использую Iesi.Collections http://www.codeproject.com/KB/recipes/sets.aspx

Он использовался во многих проектах OSS, я впервые встретил его в NHibernate

12

Я использую оболочку вокруг Dictionary<T, object>, сохраняя значения в значениях. Это дает O (1) добавлять, искать и удалять по клавишам, а для всех целей и задач действует как набор.

  • 2
    Вы должны иметь ввиду, что он примерно эквивалентен std :: unordered_set. std :: set упорядочен. Например, вы можете быстро найти начальную и конечную точки диапазона и выполнить итерацию от начала до конца, посещая элементы в порядке расположения ключей. SortedDictionary примерно соответствует станд :: установить.
11

Посмотрите PowerCollections в CodePlex. Помимо Set и OrderedSet, он имеет несколько других полезных типов коллекций, таких как Deque, MultiDictionary, Bag, OrderedBag, OrderedDictionary и OrderedMultiDictionary.

Для большего количества коллекций существует также C5 Generic Collection Library.

10

Если вы используете .NET 4.0 или новее:

В случае, если вам нужна сортировка, используйте SortedSet<T>. В противном случае, если вы этого не сделаете, используйте HashSet<T>, так как он O(1) для поиска и управления операциями. Если SortedSet<T> - O(log n) для поиска и управления операциями.

0

Через пару часов вы сможете реализовать свою работоспособную реализацию набора. Я использовал это, когда мне приходилось это делать (извините, у меня нет удобного кода): http://java.sun.com/j2se/1.4.2/docs/api/java/util/Set.html

-5

Я знаю, что это старый поток, но я столкнулся с той же проблемой и нашел HashSet очень ненадежным, потому что, учитывая одно и то же семя, GetHashCode() возвратил разные коды. Итак, я подумал, почему бы просто не использовать List и скрыть метод добавления, подобный этому

public class UniqueList<T> : List<T>
{
    public new void Add(T obj)
    {
        if(!Contains(obj))
        {
            base.Add(obj);
        }
    }
}

Поскольку List использует метод Equals исключительно для определения равенства, вы можете определить метод Equals на вашем типе T, чтобы убедиться, что вы получите желаемые результаты.

  • 13
    Причина, по которой вы не захотите использовать это, заключается в том, что List.Contains имеет сложность O(n) что означает, что ваш метод Add теперь также становится сложным O(n) . Предполагая, что внутренняя коллекция не нуждается в изменении размера, Add для List и HashMap должен иметь сложность O(1) . TLDR: Это сработает, но это хакерское и менее эффективное.
  • 6
    Конечно, если ваши объекты не возвращают подходящее значение для GetHashCode, вы не должны помещать их в контейнер на основе хеша. Было бы лучше исправить GetHashCode, чем использовать менее эффективный контейнер.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню