nesnenin bir listede zaten mevcut olup olmadığı nasıl kontrol edilir


103

Bir listem var

  List<MyObject> myList

ve bir listeye öğeler ekliyorum ve bu nesnenin zaten listede olup olmadığını kontrol etmek istiyorum.

yani bunu yapmadan önce:

 myList.Add(nextObject);

NextObject'in zaten listede olup olmadığını görmek istiyorum.

"MyObject" nesnesi bir dizi özelliğe sahiptir ancak karşılaştırma, iki özelliğin eşleşmesine dayanır.

Bu "MyObject" listesine yeni bir "MyObject" eklemeden önce bir kontrol yapmanın en iyi yolu nedir?

Düşündüğüm tek çözüm, bir listeden sözlüğe geçmek ve ardından anahtarı özelliklerin birleştirilmiş bir dizesi yapmaktı (bu biraz hoş görünmüyor).

List veya LINQ veya başka bir şey kullanan başka temiz çözümler var mı?

Yanıtlar:


153

Belirli bir durumun ihtiyaçlarına bağlıdır. Örneğin, sözlük yaklaşımı şu varsayımla oldukça iyi olacaktır:

  1. Liste nispeten kararlıdır (sözlüklerin optimize edilmediği çok sayıda ekleme / silme işlemi yoktur)
  2. Liste oldukça büyüktür (aksi takdirde sözlüğün ek yükü anlamsızdır).

Yukarıdakiler sizin durumunuz için doğru değilse, sadece yöntemi kullanın Any():

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

Bu, bir eşleşme bulana kadar veya sona ulaşıncaya kadar listede numaralandırılacaktır.


List.exists için bir yüklem delegesinin kullanılması başka bir çözümdür, aşağıya bakınız, ancak çok büyük listeleriniz varsa ve bir sözlük ile anahtar değeriniz bir karma tablo olduğu için çok daha hızlı olacaktır! Keyfini Çıkar
Doug

1
Birden çok değer nasıl kontrol edilir?
Nitin Karale

80

Basitçe İçerir yöntemini kullanın . Eşitlik işlevine göre çalıştığını unutmayınEquals

bool alreadyExist = list.Contains(item);

5
Bu benim için işe yaramadı, her zaman olmadığını söyledi
Si8

4
@ Si8 Nesneleri karşılaştırmaya çalışıyorsanız, IEquatable <T> .Equals uygulamasının nesnenizin türüne uygun şekilde uygulandığından emin olmalısınız. Aksi takdirde, nesne içeriklerini karşılaştırmayacaksınız. Bunun nasıl uygulanacağına dair bir örnek için Ahmed'in belirtilen İçerir bağlantısına bakın.
Doug Knudsen

56

Bu 2 mülkün kullanılması sürdürülebilirse, şunları yapabilirsiniz:

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

7

Bu durumda bir listeye ihtiyacınız olduğuna emin misiniz? Listeyi birçok öğe ile dolduruyorsanız, performans myList.Containsveya myList.Any; çalışma zamanı ikinci dereceden olacaktır. Daha iyi bir veri yapısı kullanmayı düşünebilirsiniz. Örneğin,

 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'i aşağıdaki şekilde kullanabilirsiniz:

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

Elbette, bu eşitlik tanımı MyClass'evrensel' ise, bir IEqualityCompareruygulama yazmanıza gerek yok ; Sadece geçersiz olabilir GetHashCodeve Equalssınıfta kendisi.


Evet, Bool for V benim favorimdi. Bu konuda, çok uzun zaman önce (eh, yaklaşık 3 hafta) HashSet'in 2.0 kodu üzerinde çalıştığım için bana ulaşamadığı ve HashSet'in Mono uygulamasını bıraktım çünkü çok kullanışlı :)
Jon Hanna 08

4

Bahsetmemiz gereken bir diğer nokta da eşitlik işlevinizin beklediğiniz gibi olmasını sağlamanız gerektiğidir. İki örneğin eşit kabul edilmesi için nesnenizin hangi özelliklerinin eşleşmesi gerektiğini ayarlamak için eşittir yöntemini geçersiz kılmalısınız.

O zaman mylist.contains (öğe) yapabilirsiniz.


3

İşte sorununuzu nasıl çözeceğinize dair kavramı anlatmak için hızlı bir konsol uygulaması.

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

Zevk almak!


3

Düzenleme: İlk önce şunu söylemiştim:


Sözlük çözümünün nezaketsizliği. Bana mükemmel bir şekilde zarif görünüyor, özellikle de sözlüğü oluştururken sadece karşılaştırıcıyı ayarlamanız gerekiyor.


Tabii ki yine de bir şeyi anahtar olarak kullanmak, aynı zamanda değer olduğunda da uygunsuzdur.

Bu nedenle bir HashSet kullanırım. Daha sonraki işlemler indekslemeyi gerektiriyorsa, Ekleme yapıldığında ondan bir liste oluşturur, aksi takdirde sadece hashset'i kullanırdım.


Bunu yalnızca nesnelerin listesi bir hash tablosundan bu yana çok büyükse ve hızlı aramalar için harikaysa kullanırdım.
Doug

0

Basit ama işe yarıyor

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

veya

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

-1

EF core kullanıyorsanız

 .UseSerialColumn();

Misal

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

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .UseSerialColumn();
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.