Fügen Sie der Liste nur eindeutige Elemente hinzu

84

Ich füge Remote-Geräte zu einer Liste hinzu, während sie sich im Netzwerk anmelden. Ich möchte das Gerät nur dann zur Liste hinzufügen, wenn es noch nicht hinzugefügt wurde.

Die Ansagen stoßen auf einen asynchronen Socket-Listener, sodass der Code zum Hinzufügen eines Geräts auf mehreren Threads ausgeführt werden kann. Ich bin mir nicht sicher, was ich falsch mache, aber egal was ich versuche, ich bekomme Duplikate. Hier ist was ich derzeit habe .....

lock (_remoteDevicesLock)
{
    RemoteDevice rDevice = (from d in _remoteDevices
                            where d.UUID.Trim().Equals(notifyMessage.UUID.Trim(), StringComparison.OrdinalIgnoreCase)
                            select d).FirstOrDefault();
     if (rDevice != null)
     {
         //Update Device.....
     }
     else
     {
         //Create A New Remote Device
         rDevice = new RemoteDevice(notifyMessage.UUID);
         _remoteDevices.Add(rDevice);
     }
}
Oli
quelle
Wofür ist die Definition RemoteDevice?
pstrjds
Können Sie zum Debuggen Ihre _remoteDevices-Klasse um das Zeitstempelfeld _remoteDevices.lastSeen = now erweitern?
Beth

Antworten:

154

Wenn Ihre Anforderungen keine Duplikate enthalten sollen, sollten Sie ein HashSet verwenden .

HashSet.Add gibt false zurück, wenn das Element bereits vorhanden ist (falls dies für Sie überhaupt von Bedeutung ist).

Sie können den Konstruktor verwenden, auf den @pstrjds unten (oder hier ) verweist , um den Gleichheitsoperator zu definieren, oder Sie müssen die Gleichheitsmethoden in RemoteDevice( GetHashCode& Equals) implementieren .

Austin Salonen
quelle
3
Wollte diese Antwort hinzufügen. Sie können diese Überladung verwenden, um den Vergleich zu definieren - msdn.microsoft.com/en-us/library/bb359100(v=vs.100).aspx
pstrjds
11
Ein wichtiger Hinweis hierbei ist, dass HashSet die Einfügereihenfolge nicht garantiert. Wenn also die Reihenfolge wichtig ist (Elemente müssen in der gleichen Reihenfolge in der Liste List<T>angezeigt werden, in der Sie sie eingegeben haben , wie z. B. was passiert ), funktioniert HashSet nicht gut.
JulianR
Vielen Dank dafür. Muss ich das Schloss zur Gewindesicherheit noch behalten oder gibt es einen besseren Weg?
Oli
2
@Oli Sie müssen das Schloss behalten, aber der Vorgang wird viel schneller sein, damit sie nicht so lange aufeinander warten. Es gibt ConcurrentSetleider keine Klasse. Es gibt jedoch eine ConcurrentDictionaryKlasse, sodass Sie diese verwenden, Ihre Werte als Schlüssel speichern und einfach nullin den Werten speichern können.
Servy
1
@Oli: Wenn ich Sie wäre, würde ich eine weitere Frage stellen, um dieses Problem zu beheben (mit der vollständigen Quelle von GetHashCode & Equals sowie Fällen, in denen sie übereinstimmen, wenn Sie dies nicht erwarten).
Austin Salonen
22
//HashSet allows only the unique values to the list
HashSet<int> uniqueList = new HashSet<int>();

var a = uniqueList.Add(1);
var b = uniqueList.Add(2);
var c = uniqueList.Add(3);
var d = uniqueList.Add(2); // should not be added to the list but will not crash the app

//Dictionary allows only the unique Keys to the list, Values can be repeated
Dictionary<int, string> dict = new Dictionary<int, string>();

dict.Add(1,"Happy");
dict.Add(2, "Smile");
dict.Add(3, "Happy");
dict.Add(2, "Sad"); // should be failed // Run time error "An item with the same key has already been added." App will crash

//Dictionary allows only the unique Keys to the list, Values can be repeated
Dictionary<string, int> dictRev = new Dictionary<string, int>();

dictRev.Add("Happy", 1);
dictRev.Add("Smile", 2);
dictRev.Add("Happy", 3); // should be failed // Run time error "An item with the same key has already been added." App will crash
dictRev.Add("Sad", 2);
RavinderReddy Seelam
quelle
16

Genau wie die akzeptierte Antwort besagt, dass ein HashSet keine Bestellung hat. Wenn die Reihenfolge wichtig ist, können Sie weiterhin eine Liste verwenden und prüfen, ob sie den Artikel enthält, bevor Sie ihn hinzufügen.

if (_remoteDevices.Contains(rDevice))
    _remoteDevices.Add(rDevice);

Das Ausführen von List.Contains () für eine benutzerdefinierte Klasse / ein benutzerdefiniertes Objekt erfordert die Implementierung IEquatable<T>in der benutzerdefinierten Klasse oder das Überschreiben der Equals. Es ist eine gute Idee, diese auch GetHashCodein der Klasse zu implementieren . Dies ist in der Dokumentation unter https://msdn.microsoft.com/en-us/library/ms224763.aspx enthalten

public class RemoteDevice: IEquatable<RemoteDevice>
{
    private readonly int id;
    public RemoteDevice(int uuid)
    {
        id = id
    }
    public int GetId
    {
        get { return id; }
    }

    // ...

    public bool Equals(RemoteDevice other)
    {
        if (this.GetId == other.GetId)
            return true;
        else
            return false;
    }
    public override int GetHashCode()
    {
        return id;
    }
}
Luke Eckley
quelle
Hallo, aber was ist, wenn Sie nicht überschreiben können, weil ich einen Verweis auf den Code eines anderen verwende - was macht man hier?
BKSpurgeon