Добавить только уникальный элемент в список

85

Я добавляю удаленные устройства в список, когда они сообщают о себе по сети. Я хочу добавить устройство в список только в том случае, если оно не было добавлено ранее.

Объявления проходят через прослушиватель асинхронных сокетов, поэтому код для добавления устройства можно запускать в нескольких потоках. Я не уверен, что делаю не так, но что бы я ни делал, я получаю дубликаты. Вот что у меня сейчас есть .....

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);
     }
}
Оли
источник
Какое определение для RemoteDevice?
pstrjds
в целях отладки можете ли вы расширить свой класс _remoteDevices с помощью поля отметки времени _remoteDevices.lastSeen = now?
Бет

Ответы:

155

Если в ваших требованиях не должно быть дубликатов, вы должны использовать HashSet .

HashSet.Add вернет false, если элемент уже существует (если это даже важно для вас).

Вы можете использовать конструктор, на который @pstrjds ссылается ниже (или здесь ), чтобы определить оператор равенства, или вам нужно будет реализовать методы равенства в RemoteDevice( GetHashCode& Equals).

Остин Салонен
источник
3
Собирался добавить этот ответ. Вы можете использовать эту перегрузку для определения сравнения - msdn.microsoft.com/en-us/library/bb359100(v=vs.100).aspx
pstrjds
11
Здесь важно отметить, что HashSet не гарантирует соблюдение порядка вставки. Поэтому, если порядок важен (элементы должны отображаться в списке в том же порядке, в котором вы их помещаете, например, что происходит с List<T>), тогда HashSet не работает.
JulianR
Большое спасибо за это. Мне все еще нужно держать замок для безопасности потоков или есть лучший способ?
Oli
2
@Oli Вам нужно держать блокировку, но операция будет намного быстрее, поэтому они не будут ждать друг друга так долго. Там нет ConcurrentSetкласса, к сожалению. Однако существует ConcurrentDictionaryкласс, поэтому вы можете использовать его, хранить свои значения в качестве ключей и просто сохранять nullзначения.
Servy
1
@Oli: На вашем месте я бы задал еще один вопрос, чтобы решить эту проблему (с полным исходным кодом как GetHashCode, так и Equals, а также со случаями, когда они совпадают, когда вы этого не ожидаете).
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

Точно так же, как принятый ответ говорит, что у HashSet нет порядка. Если порядок важен, вы можете продолжать использовать список и проверять, содержит ли он элемент, прежде чем добавлять его.

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

Выполнение List.Contains () для пользовательского класса / объекта требует реализации IEquatable<T>в пользовательском классе или переопределения Equals. Также неплохо реализовать GetHashCodeв классе. Это согласно документации на 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;
    }
}
Люк Экли
источник
привет, спасибо, но что делать, если вы не можете переопределить, потому что я использую ссылку на чужой код - что здесь делать?
BKSpurgeon