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

我有个名单

  List<MyObject> myList

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

所以在我这么做之前:

 myList.Add(nextObject);

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

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

在我添加一个新的“ MyObject”到这个“ MyObject”列表之前,最好的检查方法是什么。

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

有没有其他清洁解决方案使用列表或 LINQ 或其他东西?

479231 次浏览

这取决于具体情况的需要,例如,字典方法可以很好地假设:

  1. 该列表相对稳定(没有很多插入/删除操作,字典没有针对这些操作进行优化)
  2. 这个列表非常大(否则字典的开销是没有意义的)。

如果上述情况不适用于您的情况,只需使用方法 译自: 美国《科学》杂志网站(http://msdn.microsoft.com/en-us/library/bb534972.aspx)原著:

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

这将通过列表进行枚举,直到找到匹配项,或者直到到达结尾。

如果可以维护使用这两个属性,你可以:

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

另一点要提到的是,您应该确保您的等式函数是您所期望的。应重写 equals 方法,以设置对象的属性必须匹配哪些属性,以使两个实例被视为相等。

那你就做吧 包含(条目)

你确定你需要一份名单吗?如果您使用许多项填充列表,那么使用 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

编辑: 我首先说:


字典解决方案哪里不雅观了?在我看来它非常优雅,尤其是在创建字典时只需要设置比较器。


当然,如果某个东西也是值,那么用它作为键是不优雅的。

因此,我将使用 HashSet。如果以后的操作需要索引,我会在添加操作完成时从中创建一个列表,否则,只需使用哈希集。

这里有一个快速的控制台应用程序来描述如何解决你的问题的概念。

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

好好享受吧!

简单地使用 包含方法:

bool alreadyExist = list.Contains(item);

注意,它基于等式函数 Equals工作。如果需要实现 Equals函数,请检查 以上连结的示例。

很简单,但很管用

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

或者

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

集合可以用作字典,其区别在于不需要引用 Microsoft 脚本运行时或使用后期绑定。请注意,在这种情况下,键必须是一个字符串。在我的例子中,键(数字)是整数,但声明为字符串。 您可以创建一个自定义布尔函数来检查列表中是否存在密钥。 有一篇很好的文章是保罗 · 凯利在《 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