Solo agregar artículo único a la lista

84

Estoy agregando dispositivos remotos a una lista a medida que se anuncian en la red. Solo quiero agregar el dispositivo a la lista si no se ha agregado previamente.

Los anuncios llegan a través de un detector de socket asíncrono, por lo que el código para agregar un dispositivo se puede ejecutar en varios subprocesos. No estoy seguro de qué estoy haciendo mal, pero no importa lo que intente, termino con duplicaciones. Esto es lo que tengo actualmente ...

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
fuente
¿Para qué es la definición RemoteDevice?
pstrjds
para fines de depuración, ¿puede ampliar su clase _remoteDevices con un campo de marca de tiempo, _remoteDevices.lastSeen = ahora?
Beth

Respuestas:

154

Si sus requisitos son no tener duplicados, debe utilizar un HashSet .

HashSet.Add devolverá falso cuando el elemento ya existe (si eso te importa).

Puede utilizar el constructor que @pstrjds enlaza a continuación (o aquí ) para definir el operador de igualdad o deberá implementar los métodos de igualdad en RemoteDevice( GetHashCode& Equals).

Austin Salonen
fuente
3
Estaba a punto de agregar esta respuesta. Puede usar esta sobrecarga para definir la comparación: msdn.microsoft.com/en-us/library/bb359100(v=vs.100).aspx
pstrjds
11
Una nota importante aquí es que no se garantiza que HashSet respete el orden de inserción. Entonces, si el orden es importante (los elementos deben aparecer en la lista en el mismo orden en que los colocó, como sucede con List<T>), HashSet no funciona bien.
JulianR
Muchas gracias por esto ¿Todavía necesito mantener el candado para la seguridad de los hilos o hay una manera mejor?
Oli
2
@Oli Debes mantener la cerradura, pero la operación será mucho más rápida, por lo que no se esperarán tanto el uno al otro. ConcurrentSetDesafortunadamente, no hay clase. Sin embargo, hay una ConcurrentDictionaryclase, por lo que puede usarla, almacenar sus valores como claves y simplemente almacenar nulllos valores.
Servicio
1
@Oli: Si yo fuera usted, publicaría otra pregunta para abordar ese problema (con la fuente completa de GetHashCode y Equals, junto con los casos en los que coinciden cuando no espera que lo hagan).
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);
Ravinder como Reddy Seelam
fuente
16

Al igual que la respuesta aceptada dice que un HashSet no tiene un pedido. Si el orden es importante, puede continuar usando una Lista y verificar si contiene el artículo antes de agregarlo.

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

Realizar List.Contains () en una clase / objeto personalizado requiere implementarlo IEquatable<T>en la clase personalizada o anular el Equals. Es una buena idea implementar también GetHashCodeen la clase. Esto es por la documentación en 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;
    }
}
Luke Eckley
fuente
hola gracias, pero ¿qué pasa si no puede anular porque estoy usando una referencia al código de otra persona? ¿Qué hace uno aquí?
BKSpurgeon