如何检查列表中是否已存在对象

149
我有一个列表
  List<MyObject> myList

我正在添加列表项,并且我想检查该对象是否已在列表中。

所以在执行此操作之前:

 myList.Add(nextObject);

我想查看nextObject是否已经在列表中。

"MyObject"对象有许多属性,但比较是基于匹配两个属性进行的。

在将新的"MyObject"添加到"MyObject"列表之前,最好的方法是什么?

我唯一想到的解决方案是从列表更改为字典,然后将键设置为属性的连接字符串(这似乎有些不太优雅)。

还有没有使用列表或LINQ或其他东西的更干净的解决方案?

9个回答

196

这取决于具体情况的需要。例如,字典方法在以下情况下会非常好:

  1. 列表相对稳定(没有大量插入/删除,这不是字典优化的目标)
  2. 列表非常大(否则字典的开销是没有意义的)。

如果以上情况不适用于您的情况,请使用方法Any()

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

这将遍历列表直到找到匹配项,或者直到达到末尾。


使用谓词委托来调用list.exists是另一种解决方案,但如果你有大量的列表和键值对,使用字典会更快,因为它是一个哈希表!享受吧。 - Doug
1
如何检查多个值? - Nitin Karale
这个解决方案仅适用于 .NET 5.0+。不幸的是,我们中有些人没有可用的版本。 :-( - Darkgaze

120

只需使用Contains方法:

bool alreadyExist = list.Contains(item);
注意,它是基于相等性函数Equals工作的。如果您需要实现Equals函数,请查看上面链接的示例。

6
这对我没有起作用,它总是说它不存在。 - Si8
5
如果您想比较对象,必须确保IEquatable<T>.Equals实现对您的对象类型进行了正确实现。否则,您将无法比较对象内容。请参见Ahmad提供的Contains链接,了解如何实现此操作。 翻译:如果你想比较对象,就必须确保你的对象类型已经正确地实现了IEquatable<T>.Equals方法,否则你将无法比较对象的内容。可以参考Ahmad提供的Contains链接,了解如何实现它。 - Doug Knudsen
不能在这里使用list.contains,因为它会对dev1、dev11、dev111等进行报告真实。 - Franck E
1
@FranckE - 听起来你没有为你想要检查Contains的任何内容正确设置IEquatable<T>。 - Jesse Williams
当列表是对象列表时,它不起作用,但当列表是字符串列表时,它起作用。 - fletchsod
在我的情况下,我为我的类/接口实现了IEquatable<T>,并使用foreach遍历了一个列表,并在每个项目上使用.Exists,这在那种情况下非常有效。我用它来添加尚未在列表中的对象,这些对象通过了某些要求。 - drecunion

70
如果使用这2个属性是可维护的,你可以:
bool alreadyExists = myList.Any(x=> x.Foo=="ooo" && x.Bar == "bat");

仅限于 .Net 5+。 - Darkgaze

10

您确定在这种情况下需要列表吗?如果您正在使用myList.ContainsmyList.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 实现;您可以在类本身中重写 GetHashCodeEquals 方法。


是的,bool对于V来说是我最喜欢的。就此而言,仅仅三周前(嗯,大约三周),因为我正在处理2.0代码,HashSet对我不可用,所以我使用了Mono实现的HashSet,因为它非常有用 :) - Jon Hanna

5

另一个需要提到的点是,您应该确保您的相等函数与您的预期相同。您应该重写equals方法来设置哪些对象属性必须匹配才能被视为相等。

然后您只需要执行 mylist.contains(item)


4

简单但有效

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

或者

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

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

编辑:我之前说过:


字典解决方案有什么不优雅的地方吗?对我来说它似乎非常优雅,尤其是因为你只需要在创建字典时设置比较器。


当然,如果将某些内容既用作键又用作值,则使用这种解决方案就不够优雅。

因此,我会使用哈希集合。如果以后的操作需要索引,我会在添加完成后从中创建列表,否则就直接使用哈希集合。


只有在对象列表非常大的情况下才会使用哈希表,因为它们非常适合快速查找。 - Doug

0
一个集合可以被用作字典,不同之处在于你不需要引用Microsoft Scripting Runtime或使用late binding。请注意,在这种情况下,键必须是字符串。在我的情况下,键(数字)是整数,但声明为字符串。 您可以创建一个自定义的布尔函数来检查列表中是否存在该键。 Paul Kelly有一篇很好的文章ExcelMacroMastery.com
' Function to check if item in the collection already exists
Function Exists(coll As Collection, key As String) As Boolean

    On Error GoTo EH

    IsObject (coll.Item(key))
    Exists = True

EH:
End Function

最后你可以像这样使用它

For i = 3 To lastRow
    
        ' Ignore the Normal areas
        If rg.Cells(i, 1).value <> "Normal" Then
        
            number = rg.Cells(i, 1).value
            
            ' Check if the area exist in the collection using a custom function Exists
            If Exists(coll, number) = False Then
            
                Set oRiskArea = New clsHighRiskArea
                oRiskArea.number = number
                coll.add key:=oRiskArea.number, Item:=oRiskArea
                
            Else
                Set oRiskArea = coll(number)
            End If
            
            With oRiskArea
        
                .name = rg.Cells(i, 2).value
        
            End With
            
        End If

    Next i

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接