Додайте лише унікальний елемент до списку


84

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

Оголошення надходять через прослуховувач асинхронних сокетів, тому код для додавання пристрою можна запускати на декількох потоках. Я не впевнений, що роблю неправильно, але жодне, що я намагаюся, у підсумку дублюється. Ось те, що я зараз маю .....

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 = зараз?
Бет

Відповіді:


154

Якщо ваші вимоги не мають дублікатів, ви повинні використовувати 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

Велике спасибі за це. Мені все-таки потрібно тримати замок для захисту ниток, чи є кращий спосіб?
Олі

2
@Oli Вам потрібно тримати замок, але операція буде набагато швидшою, тому вони не будуть стільки чекати один одного. На жаль, ConcurrentSetкласу немає . Однак є ConcurrentDictionaryклас, тому ви можете ним користуватися, зберігати свої значення як ключі і просто зберігати nullу значеннях.
Серві

1
@Oli: Якби я був на вашому місці, я б опублікував ще одне запитання для вирішення цієї проблеми (із повним джерелом і GetHashCode, і рівних, а також випадків, коли вони збігаються, коли ви цього не очікуєте).
Остін Салонен

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

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

привіт thx, але що, якщо ти не можеш замінити, тому що я використовую посилання на чужий код - що тут робити?
BKSpurgeon
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.