як перевірити, чи об’єкт уже існує у списку


102

У мене є список

  List<MyObject> myList

і я додаю елементи до списку, і я хочу перевірити, чи є цей об’єкт у списку.

тому перш ніж зробити це:

 myList.Add(nextObject);

Я хочу побачити, чи є наступнийОб'єкт у списку.

Об'єкт "MyObject" має ряд властивостей, але порівняння засноване на збігу двох властивостей.

Який найкращий спосіб зробити перевірку, перш ніж я додати новий "MyObject" до цього списку "MyObject".

Єдине рішення, яке я придумав, - це перейти зі списку до словника, а потім зробити ключ об'єднаним рядком властивостей (це здається трохи неелегантним).

Будь-які інші чистіші рішення, використовуючи список, LINQ або щось інше?

Відповіді:


153

Це залежить від потреб конкретної ситуації. Наприклад, словниковий підхід був би досить хорошим, якщо припустити:

  1. Список відносно стабільний (не багато вставок / видалень, для яких словники не оптимізовані)
  2. Список досить великий (інакше накладні дані словника безглузді).

Якщо вищезгадане не відповідає дійсності для вашої ситуації, просто скористайтеся методом Any():

Item wonderIfItsPresent = ...
bool containsItem = myList.Any(item => item.UniqueProperty == wonderIfItsPresent.UniqueProperty);

Це буде перераховано через список до тих пір, поки він не знайде відповідність або поки не досягне кінця.


Використання делегата предикатів для list.exists - це ще одне рішення, див. Нижче, але якщо у вас є величезні списки та ключове значення зі словником, буде набагато швидше, оскільки це хеш-таблиця! Насолоджуйтесь
Даг

1
Як перевірити кратне значення?
Нітін Карале

80

Просто використовуйте метод Містить . Зауважте, що він працює на основі функції рівностіEquals

bool alreadyExist = list.Contains(item);

5
Це не спрацювало для мене, він завжди говорив, що його немає
Si8,

4
@ Si8 Якщо ви намагаєтеся порівняти об'єкти, ви повинні бути впевнені, що реалізація IEquatable <T> .Equals реалізована належним чином для типу вашого об'єкта. В іншому випадку ви не будете порівнювати вміст об'єкта. Див. Посилання "Містить", яке Ахмад вказав для прикладу того, як це реалізувати.
Doug Knudsen

56

Якщо використовувати ці 2 властивості, можна:

bool alreadyExists = myList.Any(x=> x.Foo=="ooo" && x.Bar == "bat");

7

Ви впевнені, що вам потрібен список у цьому випадку? Якщо ви заповнюєте список багатьма елементами, продуктивність постраждає з myList.Containsабо myList.Any; час роботи буде квадратичним. Ви можете розглянути можливість використання кращої структури даних. Наприклад,

 public class MyClass
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }

    }

    public class MyClassComparer : EqualityComparer<MyClass>
    {
        public override bool Equals(MyClass x, MyClass y)
        {
            if(x == null || y == null)
               return x == y;

            return x.Property1 == y.Property1 && x.Property2 == y.Property2;
        }

        public override int GetHashCode(MyClass obj)
        {
            return obj == null ? 0 : (obj.Property1.GetHashCode() ^ obj.Property2.GetHashCode());
        }
    }

Ви можете використовувати HashSet таким чином:

  var set = new HashSet<MyClass>(new MyClassComparer());
  foreach(var myClass in ...)
     set.Add(myClass);

Звичайно, якщо це визначення рівності для MyClass"універсальне", вам не потрібно писати IEqualityComparerреалізацію; ви могли просто перекрити GetHashCodeі Equalsв самому класі.


Так, bool для V був моїм улюбленим. З цього приводу, не так давно (приблизно, 3 тижні) HashSet мені не був доступний, оскільки я працював над кодом 2.0, і я відмовився від Mono втілення HashSet, оскільки це так корисно :)
Джон Ханна

4

Ще один момент, який слід зазначити, - це те, що ви повинні забезпечити, щоб ваша функція рівності була такою, як ви очікували. Вам слід перекрити метод рівняння, щоб встановити, які властивості вашого об'єкта повинні відповідати, щоб два екземпляри вважалися рівними.

Тоді ви можете просто зробити mylist.contains (item)


3

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    public class myobj
    {
        private string a = string.Empty;
        private string b = string.Empty;

        public myobj(string a, string b)
        {
            this.a = a;
            this.b = b;
        }

        public string A
        {
            get
            {
                return a;
            }
        }

        public string B
        {
            get
            {
                return b;
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            List<myobj> list = new List<myobj>();
            myobj[] objects = { new myobj("a", "b"), new myobj("c", "d"), new myobj("a", "b") };


            for (int i = 0; i < objects.Length; i++)
            {
                if (!list.Exists((delegate(myobj x) { return (string.Equals(x.A, objects[i].A) && string.Equals(x.B, objects[i].B)) ? true : false; })))
                {
                    list.Add(objects[i]);
                }
            }
        }
    }
}

Насолоджуйтесь!


3

Редагувати: Я вперше сказав:


Що не важливо в словниковому рішенні. Мені це здається ідеально елегантним, тому що вам потрібно лише встановити компаратор при створенні словника.


Звичайно, неелегантно використовувати щось як ключове, коли воно також є цінністю.

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


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

0

Просто, але це працює

MyList.Remove(nextObject)
MyList.Add(nextObject)

або

 if (!MyList.Contains(nextObject))
    MyList.Add(nextObject);

-1

Якщо ви використовуєте EF core, додайте

 .UseSerialColumn();

Приклад

modelBuilder.Entity<JobItem>(entity =>
        {
            entity.ToTable("jobs");

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .UseSerialColumn();
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.