Sammlung, die nur eindeutige Elemente in .NET zulässt?

102

Gibt es eine Sammlung in C #, mit der Sie keine doppelten Elemente hinzufügen können? Zum Beispiel mit der dummen Klasse von

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

Der folgende Code löst (offensichtlich) eine Ausnahme aus:

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

Aber gibt es eine Klasse, die ebenfalls die Einzigartigkeit garantiert, jedoch ohne KeyValuePairs? Ich dachte, ich HashSet<T>würde das tun, aber nachdem ich die Dokumente gelesen habe, scheint es, dass die Klasse nur eine festgelegte Implementierung ist ( siehe Abbildung ).

Adam Rackis
quelle
4
Ich verstehe dein Problem nicht mit HashSet<T>. MSDN sagt: "Die HashSet <T> -Klasse bietet leistungsstarke Mengenoperationen. Eine Menge ist eine Sammlung, die keine doppelten Elemente enthält und deren Elemente in keiner bestimmten Reihenfolge sind."
Daniel Hilgarth
5
Können Sie mehr erklären, warum dies HashSet<T>nicht ausreicht?
JaredPar
@mootinator: Die Dictionary<K,V>Klasse nicht garantieren , jede Art von Ordnung.
LukeH
3
Ich denke, er möchte nur eine Ausnahme HashSet<T>.Addfalse
auslösen,
2
Es wird außerdem dringend empfohlen, nur solche für unveränderliche Typen zu überladen . Ein veränderlicher Kunde würde normalerweise besser mit der Standardreferenzgleichheit umgehen.
Henk Holterman

Antworten:

204

HashSet<T>ist was du suchst. Von MSDN (Hervorhebung hinzugefügt):

Die HashSet<T>Klasse bietet leistungsstarke Set-Operationen. Ein Satz ist eine Sammlung, die keine doppelten Elemente enthält und deren Elemente in keiner bestimmten Reihenfolge vorliegen.

Beachten Sie, dass die HashSet<T>.Add(T item)Methode a bool- zurückgibt, truewenn das Element zur Sammlung hinzugefügt wurde. falsewenn der Artikel bereits vorhanden war.

Krapfen
quelle
9
T item sollte in diesem Fall eine IEquatable-Schnittstelle implementieren. Wenn die Klasse diese Schnittstelle nicht erbt, fügt HashSet <T> doppelte Elemente hinzu.
Rudolf Dvoracek
Anstelle der Implementierung des Elements IEquatablekönnen Sie auch eine (benutzerdefinierte) Implementierung der EqualityComparer<T>Instanz an den HashSet<T>Konstruktor übergeben.
Sipke Schoorstra
17

Wie wäre es nur mit einer Erweiterungsmethode in HashSet?

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

Von der HashSet<T>Seite auf MSDN:

Die HashSet (Of T) -Klasse bietet leistungsstarke Set-Operationen. Ein Satz ist eine Sammlung, die keine doppelten Elemente enthält und deren Elemente in keiner bestimmten Reihenfolge vorliegen.

(Hervorhebung von mir)

Oded
quelle
4

Wenn Sie lediglich die Eindeutigkeit von Elementen sicherstellen möchten, ist HashSet genau das Richtige für Sie.

Was meinst du mit "nur eine festgelegte Implementierung"? Eine Menge ist (per Definition) eine Sammlung eindeutiger Elemente, die die Elementreihenfolge nicht speichert.

Lloyd
quelle
Du hast vollkommen recht; Die Frage war irgendwie dumm. Grundsätzlich suchte ich nach etwas, das beim Hinzufügen eines Duplikats eine Ausnahme auslöst (wie Dictionary <TKey, TValue>), aber wie bereits erwähnt, gibt HashSet <T> bei einem Duplikat add false zurück. +1, danke.
Adam Rackis
3

Nur um meine 2 Cent hinzuzufügen ...

Wenn Sie eine ValueExistingException-Auslösung benötigen, können HashSet<T>Sie Ihre Sammlung auch einfach erstellen:

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

Dies kann zum Beispiel nützlich sein, wenn Sie es an vielen Orten benötigen ...

digEmAll
quelle
Sicher. Ich habe mich gefragt, ob etwas eingebaut ist, aber danke +1
Adam Rackis
0

Sie können wie folgt in eine Art eindeutige Liste schauen

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

und Sie können es wie folgt verwenden

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

wird nur "abc","def","ghi","jkl","mno"immer zurückgegeben, auch wenn Duplikate hinzugefügt werden

Vinod Srivastav
quelle