как проверить, существует ли уже объект в списке


103

У меня есть список

  List<MyObject> myList

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

поэтому, прежде чем я это сделаю:

 myList.Add(nextObject);

Я хочу увидеть, есть ли nextObject уже в списке.

Объект «MyObject» имеет ряд свойств, но сравнение основано на сопоставлении двух свойств.

Как лучше всего проверить, прежде чем я добавлю новый «MyObject» в этот список «MyObject».

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

Любые другие более чистые решения, использующие список или LINQ или что-то еще?

Ответы:


153

Это зависит от потребностей конкретной ситуации. Например, подход со словарем был бы неплохим, если бы:

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

Если приведенное выше не соответствует вашей ситуации, просто используйте метод Any():

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

Он будет проходить по списку, пока не найдет совпадение или пока не достигнет конца.


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

1
Как проверить множественное значение?
Нитин Карале

80

Просто используйте метод Contains . Обратите внимание, что он работает на основе функции равенстваEquals

bool alreadyExist = list.Contains(item);

5
У меня это не сработало, всегда говорилось, что этого не существует
Si8,

4
@ Si8 Если вы пытаетесь сравнить объекты, вы должны быть уверены, что реализация IEquatable <T> .Equals правильно реализована для вашего типа объекта. В противном случае вы не будете сравнивать содержимое объекта. См. Ссылку "Содержит", указанную Ахмадом, чтобы увидеть, как это реализовать.
Дуг Кнудсен

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, и я отказался от реализации HashSet Mono, потому что это чертовски полезно :)
Джон Ханна,

4

Также следует упомянуть, что вы должны убедиться, что ваша функция равенства соответствует вашим ожиданиям. Вы должны переопределить метод equals, чтобы указать, какие свойства вашего объекта должны совпадать, чтобы два экземпляра считались равными.

Тогда вы можете просто сделать 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, добавьте

 .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.