Колекція, яка дозволяє лише унікальні предмети в .NET?


103

Чи є колекція в C #, яка не дозволить вам додавати до неї повторювані елементи? Наприклад, з дурним класом о

public class Customer {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }

    public override int GetHashCode() {
        return (FirstName + LastName + Address).GetHashCode();
    }

    public override bool Equals(object obj) {
        Customer C = obj as Customer;
        return C != null && String.Equals(this.FirstName, C.FirstName) && String.Equals(this.LastName, C.LastName) && String.Equals(this.Address, C.Address);
    }
}

Наступний код (очевидно) буде винятком:

Customer Adam = new Customer { Address = "A", FirstName = "Adam", LastName = "" };
Customer AdamDup = new Customer { Address = "A", FirstName = "Adam", LastName = "" };

Dictionary<Customer, bool> CustomerHash = new Dictionary<Customer, bool>();
CustomerHash.Add(Adam, true);
CustomerHash.Add(AdamDup, true);

Але чи є клас, який аналогічно гарантуватиме унікальність, але без KeyValuePairs? Я думав, що HashSet<T>це зробить, але прочитавши документи, здається, що клас - це лише набір реалізацій ( перейти до фігури ).


4
Я не розумію вашої проблеми HashSet<T>. MSDN каже: "Клас HashSet <T> забезпечує високоефективні операції з набором. Набір - це колекція, яка не містить дублікатів елементів і елементи яких не мають конкретного порядку".
Даніель Гілгарт

5
Чи можете ви пояснити більше, чому HashSet<T>це недостатньо?
JaredPar

@mootinator: Dictionary<K,V>Клас не гарантує будь-якого замовлення.
ЛукаХ

3
Я думаю, що він просто хоче винести виняток, коли ви намагаєтеся додати існуюче значення ... Для цього просто перевірте значення bool, яке повертається з HashSet<T>.Addметоду, і кинути, коли false...
digEmAll

2
Також настійно рекомендуємо перевантажувати лише ті, що змінюються лише для типів. Клієнт, що змінюється, зазвичай справляється краще зі стандартною рівністю за замовчуванням.
Хенк Холтерман

Відповіді:


205

HashSet<T>це те, що ви шукаєте. Від MSDN (наголос додано):

HashSet<T>Клас надає набір операцій з високою продуктивністю. Набір - це колекція, яка не містить дублікатів , а елементи яких не мають особливого порядку.

Зауважте, що HashSet<T>.Add(T item)метод повертає a bool- trueякщо елемент був доданий до колекції; falseякщо предмет вже був присутній


9
Елемент T в цьому випадку повинен використовувати інтерфейс IEquatable. Якщо клас не успадковує цей інтерфейс, HashSet <T> додає повторювані елементи.
Рудольф Дворачек

Або замість реалізації елемента IEquatable, ви можете передати конструктору (власну) реалізацію EqualityComparer<T>екземпляра HashSet<T>.
Sipke Schoorstra

17

Як щодо просто методу розширення на HashSet?

public static void AddOrThrow<T>(this HashSet<T> hash, T item)
{
    if (!hash.Add(item))
        throw new ValueExistingException();
}

13

Зі HashSet<T>сторінки на MSDN:

Клас HashSet (Of T) забезпечує високопродуктивні операції з набору. Набір - це колекція, яка не містить дублікатів , а елементи яких не мають особливого порядку.

(наголос мій)


4

Якщо все, що вам потрібно, це забезпечити унікальність елементів, то HashSet - це те, що вам потрібно.

Що ви маєте на увазі, коли говорите "просто набір реалізації"? Набір - це (за визначенням) сукупність унікальних елементів, які не зберігають порядок елементів.


Ви абсолютно праві; питання було якесь дурне. В основному, я шукав щось, що кине виняток, коли доданий дублікат (наприклад, словник <TKey, TValue>), але, як уже згадувалося, HashSet <T> повертає помилку на повторне додавання. +1, дякую.
Адам Ракіс


3

Тільки щоб додати мої 2 копійки ...

якщо вам потрібен показник ValueExistingException, HashSet<T>ви також можете легко створити свою колекцію:

public class ThrowingHashSet<T> : ICollection<T>
{
    private HashSet<T> innerHash = new HashSet<T>();

    public void Add(T item)
    {
        if (!innerHash.Add(item))
            throw new ValueExistingException();
    }

    public void Clear()
    {
        innerHash.Clear();
    }

    public bool Contains(T item)
    {
        return innerHash.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        innerHash.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return innerHash.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        return innerHash.Remove(item);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return innerHash.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

це може бути корисно, наприклад, якщо вам це потрібно в багатьох місцях ...


Звичайно. Мені було цікаво, чи щось вбудоване, але дякую +1
Адам Ракіс

0

Ви можете заглянути щось подібне до Унікального списку таким чином

public class UniqueList<T>
{
    public List<T> List
    {
        get;
        private set;
    }
    List<T> _internalList;

    public static UniqueList<T> NewList
    {
        get
        {
            return new UniqueList<T>();
        }
    }

    private UniqueList()
    {            
        _internalList = new List<T>();
        List = new List<T>();
    }

    public void Add(T value)
    {
        List.Clear();
        _internalList.Add(value);
        List.AddRange(_internalList.Distinct());
        //return List;
    }

    public void Add(params T[] values)
    {
        List.Clear();
        _internalList.AddRange(values);
        List.AddRange(_internalList.Distinct());
       // return List;
    }

    public bool Has(T value)
    {
        return List.Contains(value);
    }
}

і ви можете використовувати його наступним чином

var uniquelist = UniqueList<string>.NewList;
uniquelist.Add("abc","def","ghi","jkl","mno");
uniquelist.Add("abc","jkl");
var _myList = uniquelist.List;

повертається "abc","def","ghi","jkl","mno"завжди, навіть коли до нього додаються дублікати

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