检查一个 List<T> 是否包含另一个 List 中的任何元素

152

我有一个像这样的参数列表:

public class parameter
{
    public string name {get; set;}
    public string paramtype {get; set;}
    public string source {get; set;}
}

IEnumerable<Parameter> parameters;

还有一个字符串数组我想要与之进行比较。

string[] myStrings = new string[] { "one", "two"};
我希望能够迭代参数列表并检查源属性是否等于myStrings数组中的任何一个。我可以使用嵌套的foreach来完成此操作,但是我想学习如何以更好的方式完成,因为我一直在玩弄Linq,并且喜欢可枚举的扩展方法(如where等),因此嵌套的foreach感觉不太对。是否有更优雅的Linq/Lambda/Delegate方法来做到这一点呢?
谢谢。
4个回答

330

您可以使用嵌套的 Any() 进行此检查,它适用于任何 Enumerable

bool hasMatch = myStrings.Any(x => parameters.Any(y => y.source == x));

在处理更大的集合时,更快的方法是将parameters投影到source中,然后使用Intersect函数。该函数内部使用HashSet<T>,因此可以将第一种方法的O(n^2)(相当于两个嵌套循环)检查时间缩短为O(n):

bool hasMatch = parameters.Select(x => x.source)
                          .Intersect(myStrings)
                          .Any(); 

此外,作为一个旁注,你应该将你的类名和属性名大写,以符合C#样式指南。


谢谢,这似乎是我正在寻找的,我会试一下。需要更多地尝试功能方面的东西。关于大写类和属性,我确实做了,只是在上面的示例中忘记了。 - gdp
1
为什么是O(n^2)?我们不是在谈论两个变量而不是一个吗,难道不应该是O(n*m)吗?由于m(参数)是一个常数,所以它与O(n)相同。我不明白为什么这里的交集应该更快?但是同意,交集有可能更快,但不能保证。 - Squazz
1
你说得对,时间复杂度应该是O(n*m) - 其中m不是一个常数 - 它是其中一个列表的大小,即使在给定的特定示例中它可能是“2”。然而,即使是常数值在实践中也不可忽略 - 对于所有非平凡的列表长度,“Intersect”都会更快 - 如果列表非常短,那么无论如何都没有关系(在这种情况下,性能可能根本不是您关心的问题)。 - BrokenGlass
3
从性能角度考虑,parameters.Any(x => myStrings.Contains(x.source));不是比你的第一个示例更好吗? - Fluppe
1
注意:从.NET 6开始,有一个名为IntersectBy()的扩展方法,它消除了首先对源进行投影的必要性-https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.intersectby - Rand Random
显示剩余2条评论

11

这里有一个示例,用于查找另一个列表中是否存在匹配元素

List<int> nums1 = new List<int> { 2, 4, 6, 8, 10 };
List<int> nums2 = new List<int> { 1, 3, 6, 9, 12};

if (nums1.Any(x => nums2.Any(y => y == x)))
{
    Console.WriteLine("There are equal elements");
}
else
{
    Console.WriteLine("No Match Found!");
}

5
请注意,如果涉及的列表很大,这种方法会比“Intersect”方法慢得多,因为它在列表大小方面是O(N*M)。(但它的内存消耗是O(1)。) - Jon Skeet

6
list1.Select(l1 => l1.Id).Intersect(list2.Select(l2 => l2.Id)).ToList();
var list1 = await _service1.GetAll();
var list2 = await _service2.GetAll();

// Create a list of Ids from list1
var list1_Ids = list1.Select(l => l.Id).ToList();

// filter list2 according to list1 Ids
var list2 = list2.Where(l => list1_Ids.Contains(l.Id)).ToList();

1
虽然这段代码片段可能解决了问题,但包括解释将有助于人们理解您的代码建议的原因。 - Gerhard
1
为了完备起见,你可能应该添加一行检查列表2的长度的代码。使用Intersect可能也会更快,不是吗? - General Grievance

6

如果两个列表都很大,并且使用lambda表达式时需要很长时间才能获取。在这种情况下最好使用LINQ来获取参数列表:

var items = (from x in parameters
                join y in myStrings on x.Source equals y
                select x)
            .ToList();

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