Aggiungi solo elemento unico all'elenco


85

Sto aggiungendo dispositivi remoti a un elenco mentre si annunciano attraverso la rete. Voglio solo aggiungere il dispositivo all'elenco se non è stato aggiunto in precedenza.

Gli annunci stanno arrivando attraverso un listener di socket asincrono, quindi il codice per aggiungere un dispositivo può essere eseguito su più thread. Non sono sicuro di cosa sto facendo di sbagliato, ma non importa cosa provo, finisco con le duplicazioni. Ecco cosa ho attualmente .....

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

Qual è la definizione per RemoteDevice?
pstrjds

per scopi di debug, puoi estendere la tua classe _remoteDevices con un campo timestamp, _remoteDevices.lastSeen = now?
Beth

Risposte:


155

Se le tue esigenze sono di non avere duplicati, dovresti usare un HashSet .

HashSet.Add restituirà false quando l'elemento esiste già (se anche questo è importante per te).

È possibile utilizzare il costruttore a cui si collega @pstrjds di seguito (o qui ) per definire l'operatore di uguaglianza oppure sarà necessario implementare i metodi di uguaglianza in RemoteDevice( GetHashCode& Equals).


3
Stavo per aggiungere questa risposta. È possibile utilizzare questo overload per definire il confronto - msdn.microsoft.com/en-us/library/bb359100(v=vs.100).aspx
pstrjds

11
Una nota importante qui è che non è garantito che HashSet rispetti l'ordine di inserzione. Quindi, se l'ordine è importante (gli elementi devono apparire nell'elenco nello stesso ordine in cui li hai inseriti, come quello che succede con List<T>) allora HashSet non funziona bene.
JulianR

Molte grazie per questo. Devo comunque mantenere il blocco per la sicurezza dei thread o esiste un modo migliore?
Oli

2
@Oli Devi tenere il lucchetto, ma l'operazione sarà molto più rapida, quindi non si aspetteranno tanto. Non c'è ConcurrentSetlezione, purtroppo. C'è tuttavia una ConcurrentDictionaryclasse, quindi puoi usarla, memorizzare i tuoi valori come chiavi e semplicemente memorizzare nulli valori.
Servizio

1
@Oli: Se fossi in te, pubblicherei un'altra domanda per risolvere il problema (con la fonte completa di GetHashCode ed Equals, insieme ai casi in cui corrispondono quando non ti aspetti che lo facciano).
Austin Salonen

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

16

Proprio come la risposta accettata dice che un HashSet non ha un ordine. Se l'ordine è importante, puoi continuare a utilizzare un elenco e controllare se contiene l'elemento prima di aggiungerlo.

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

L'esecuzione di List.Contains () su una classe / oggetto personalizzato richiede l'implementazione IEquatable<T>sulla classe personalizzata o l'override di Equals. È una buona idea implementare anche GetHashCodenella classe. Questo è per la documentazione su https://msdn.microsoft.com/en-us/library/ms224763.aspx

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

ciao grazie ma cosa succede se non è possibile eseguire l'override perché sto usando un riferimento al codice di qualcun altro - cosa si fa qui?
BKSpurgeon
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.