如何查找列表中的元素是否在另一个列表中?

时间:2011-02-20 22:53:05

标签: c# performance linq

我想知道第一个列表中是否至少有一个元素可以在第二个列表中找到。

我可以看到两种方法。假设我们的列表是:

List<string> list1 = new[] { "A", "C", "F", "H", "I" };
List<string> list2 = new[] { "B", "D", "F", "G", "I" };

第一种方法使用循环:

bool isFound = false;
foreach (item1 in list1)
{
    if (list2.Contains(item1))
    {
        isFound = true;
        break;
    }
}

第二个直接使用Linq:

bool isFound = list1.Intersect(list2).Any();

第一个是写的很长,而不是非常直接/易于阅读。第二个是短而清晰的,但表现很低,特别是在大型名单上。

这可能是一种优雅的方式吗?

5 个答案:

答案 0 :(得分:19)

第二个在大型列表上的性能优于第一个。 Intersect将一个列表的元素放入哈希表中,然后再检查其他列表的成员资格。

答案 1 :(得分:9)

当原始显然(最坏的情况)O(n * m)时,批评LINQ的性能似乎很奇怪; LINQ方法我希望在列表中使用HashSet<T>,然后使用流迭代器块 - 所以性能应该是O(n + m) - 即更好。

答案 2 :(得分:6)

我认为第二个对于大型列表来说会更快。由于第一个是O(list1.Count * list2.Count),而第二个是O(list1.Count + list2.Count)。第二个需要更多的记忆。

linq的开销通常是手工编码的常数倍增因子。我猜第二个比命令式代码慢多了两倍,甚至可能不是。它使用O(list1.Count+list2.Count)内存,如果您仔细编写代码以便在保持线性性能的情况下使用内存不足,则可以减少O(Min(list1,list2))

此代码在大型列表中应该相对较快:

bool isFound = false;
HashSet<string> set2=new HashSet<string>(list2);
foreach (item1 in list1)
{
    if (set2.Contains(item1))
    {
        isFound = true;
        break;
    }
}

您可以通过将较小的列表放入哈希集而不是始终使用list2来进一步优化此代码。

答案 3 :(得分:4)

接受的答案很棒,但它不适用于Linq-to-sql,因为Intersect没有映射。 在这种情况下,您应该使用:

bool isFound = table.Any(row => list2.Contains(row.FieldWithValue));

这会被编译为WHERE EXSITS

答案 4 :(得分:0)

这是另一种了解一个列表中的元素是否存在于另一个列表中的方法。

bool present = List1.Any(t => List2.Any(y => y == t));