Raccolta che consente solo oggetti unici in .NET?


103

Esiste una raccolta in C # che non ti consente di aggiungere elementi duplicati? Ad esempio, con la stupida classe di

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

Il codice seguente genererà (ovviamente) un'eccezione:

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

Ma esiste una classe che garantirà allo stesso modo l'unicità, ma senza KeyValuePairs? Pensavo di HashSet<T>farlo, ma dopo aver letto i documenti sembra che la classe sia solo un'implementazione impostata ( vai a figura ).


4
Non capisco il tuo problema con HashSet<T>. MSDN dice "La classe HashSet <T> fornisce operazioni sugli insiemi ad alte prestazioni. Un insieme è una raccolta che non contiene elementi duplicati e i cui elementi non sono in un ordine particolare".
Daniel Hilgarth

5
Puoi spiegare di più perché HashSet<T>è insufficiente?
JaredPar

@mootinator: la Dictionary<K,V>classe non garantisce alcun tipo di ordine.
LukeH

3
Immagino che voglia solo lanciare un'eccezione quando provi ad aggiungere un valore esistente ... Per fare ciò, controlla il valore bool restituito dal HashSet<T>.Addmetodo e lancia quando false...
digEmAll

2
Si consiglia inoltre vivamente di sovraccaricare solo quelli per i tipi immutabili . Un cliente mutevole di solito sarebbe migliore con l'uguaglianza di riferimento predefinita.
Henk Holterman

Risposte:


205

HashSet<T>è quello che stai cercando. Da MSDN (enfasi aggiunta):

La HashSet<T>classe fornisce operazioni sugli insiemi ad alte prestazioni. Un set è una raccolta che non contiene elementi duplicati e i cui elementi non sono in un ordine particolare.

Notare che il HashSet<T>.Add(T item)metodo restituisce un bool- truese l'elemento è stato aggiunto alla raccolta; falsese l'articolo era già presente.


9
L'elemento T in questo caso dovrebbe implementare l'interfaccia IEquatable. Se la classe non eredita questa interfaccia, HashSet <T> aggiunge elementi duplicati.
Rudolf Dvoracek

O invece dell'implementazione dell'elemento IEquatable, è possibile passare un'implementazione (personalizzata) EqualityComparer<T>dell'istanza al HashSet<T>costruttore.
Sipke Schoorstra

17

Che ne dici di un metodo di estensione su HashSet?

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

13

Dalla HashSet<T>pagina su MSDN:

La classe HashSet (Of T) fornisce operazioni sugli insiemi ad alte prestazioni. Un set è una raccolta che non contiene elementi duplicati e i cui elementi non sono in un ordine particolare.

(enfasi mia)


4

Se tutto ciò di cui hai bisogno è garantire l'unicità degli elementi, HashSet è ciò di cui hai bisogno.

Cosa intendi quando dici "solo una serie di implementazioni"? Un insieme è (per definizione) una raccolta di elementi unici che non salva l'ordine degli elementi.


Hai completamente ragione; la domanda era un po 'stupida. Fondamentalmente, stavo cercando qualcosa che generasse un'eccezione quando veniva aggiunto un duplicato (come Dictionary <TKey, TValue>), ma come già accennato, HashSet <T> restituisce false su un duplicato add. +1, grazie.
Adam Rackis


3

Solo per aggiungere i miei 2 centesimi ...

se hai bisogno di un ValueExistingException-throwing HashSet<T>puoi anche creare facilmente la tua collezione:

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

questo può essere utile ad esempio se ne hai bisogno in molti posti ...


Sicuro. Mi chiedevo se qualcosa fosse integrato, ma grazie +1
Adam Rackis

0

Puoi esaminare una specie di Elenco univoco come segue

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

e puoi usarlo come segue

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

tornerà "abc","def","ghi","jkl","mno"sempre solo anche quando vengono aggiunti duplicati

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.