วิธีตรวจสอบว่ามีวัตถุอยู่แล้วในรายการหรือไม่


104

ฉันมีรายชื่อ

  List<MyObject> myList

และฉันกำลังเพิ่มรายการในรายการและฉันต้องการตรวจสอบว่ามีวัตถุนั้นอยู่ในรายการหรือไม่

ก่อนที่ฉันจะทำสิ่งนี้:

 myList.Add(nextObject);

ฉันต้องการดูว่า nextObject อยู่ในรายการหรือไม่

วัตถุ "MyObject" มีคุณสมบัติหลายอย่าง แต่การเปรียบเทียบจะขึ้นอยู่กับการจับคู่กับคุณสมบัติสองอย่าง

วิธีใดเป็นวิธีที่ดีที่สุดในการตรวจสอบก่อนที่ฉันจะเพิ่ม "MyObject" ใหม่ในรายการ "MyObject" นี้

ทางออกเดียวที่ฉันคิดขึ้นคือเปลี่ยนจากรายการเป็นพจนานุกรมจากนั้นทำให้คีย์เป็นสตริงคุณสมบัติที่ต่อกัน (ดูเหมือนจะไม่สง่างามเล็กน้อย)

โซลูชันอื่น ๆ ที่สะอาดกว่าโดยใช้รายการหรือ LINQ หรืออย่างอื่น?

คำตอบ:


154

ขึ้นอยู่กับความต้องการของสถานการณ์เฉพาะ ตัวอย่างเช่นแนวทางพจนานุกรมจะค่อนข้างดีโดยสมมติว่า:

  1. รายการค่อนข้างเสถียร (มีการแทรก / ลบไม่มากซึ่งพจนานุกรมไม่ได้รับการปรับให้เหมาะสม)
  2. รายการมีขนาดค่อนข้างใหญ่ (มิฉะนั้นค่าใช้จ่ายของพจนานุกรมจะไม่มีจุดหมาย)

หากข้างต้นไม่เป็นจริงสำหรับสถานการณ์ของคุณให้ใช้วิธีการAny():

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

สิ่งนี้จะแจกแจงรายการจนกว่าจะพบรายการที่ตรงกันหรือจนกว่าจะถึงจุดสิ้นสุด


การใช้เพรดิเคตตัวแทนสำหรับ list.exists เป็นอีกวิธีการแก้ปัญหาด้านล่าง แต่ถ้าคุณมีรายการขนาดใหญ่และค่าคีย์พร้อมพจนานุกรมจะเร็วกว่ามากเนื่องจากเป็นตารางแฮช! Enjoy
Doug

1
จะตรวจสอบหลายค่าได้อย่างไร?
Nitin Karale

81

เพียงใช้วิธีการประกอบด้วย โปรดทราบว่ามันทำงานตามฟังก์ชันความเท่าเทียมกันEquals

bool alreadyExist = list.Contains(item);

5
สิ่งนี้ไม่ได้ผลสำหรับฉันมันมักจะบอกว่ามันไม่มีอยู่
Si8

4
@ Si8 หากคุณกำลังพยายามเปรียบเทียบออบเจ็กต์คุณต้องแน่ใจว่าการติดตั้ง IEquatable <T> .Equals ถูกนำไปใช้อย่างถูกต้องสำหรับประเภทของวัตถุของคุณ มิฉะนั้นคุณจะไม่เปรียบเทียบเนื้อหาของวัตถุ ดูลิงค์ประกอบด้วย Ahmad ที่ระบุสำหรับตัวอย่างวิธีการใช้งาน
Doug Knudsen


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ในคลาสได้เอง


ใช่บูลสำหรับ V เป็นรายการโปรดของฉัน สำหรับเรื่องนั้นเมื่อไม่นานมานี้ (เอ๊ะประมาณ 3 สัปดาห์) ที่ HashSet ไม่สามารถใช้ได้กับฉันเพราะฉันกำลังทำงานกับโค้ด 2.0 และฉันได้ลดการใช้งาน HashSet แบบ Mono เพราะมันมีประโยชน์มาก :)
Jon Hanna

4

อีกประเด็นที่ต้องพูดถึงคือคุณควรตรวจสอบให้แน่ใจว่าฟังก์ชันความเท่าเทียมกันของคุณเป็นไปตามที่คุณคาดหวัง คุณควรลบล้างเมธอดเท่ากับเพื่อตั้งค่าคุณสมบัติของอ็อบเจ็กต์ของคุณที่จะต้องจับคู่สองอินสแตนซ์จึงจะถือว่าเท่ากัน

จากนั้นคุณสามารถทำ mylist.contains (รายการ)


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 หากการดำเนินการในภายหลังจำเป็นต้องมีการจัดทำดัชนีฉันจะสร้างรายการจากรายการเมื่อการเพิ่มเสร็จสิ้นมิฉะนั้นเพียงแค่ใช้แฮชเซ็ต


ฉันจะใช้สิ่งนี้ก็ต่อเมื่อรายการวัตถุมีขนาดใหญ่เนื่องจากเป็นตารางแฮชและเหมาะสำหรับการค้นหาอย่างรวดเร็ว
Doug


-1

หากคุณใช้ EF core add

 .UseSerialColumn();

ตัวอย่าง

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

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .UseSerialColumn();
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.