One of the programming scenarios we can encounter once in a while is checking whether certain items in a list exist in another. This article is going to dive deep into all the techniques to compare lists. We are also going to compare how these techniques perform and discuss some of the best practices and considerations that can make our solutions as efficient as possible. 

To download the source code for this article, you can visit our GitHub repository.

Without further ado, let’s start!

Use Iteration to Check if Items Exist in Another List in C#

We can use iteration to check whether items exist in another list in C#:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!
foreach (var item in firstList)
{
    if (secondList.Contains(item))
    {
        return true;
    }
}

return false;

Assuming we have two lists firstList and secondList, we loop through firstList as we check whether any element exists in secondList and exit the loop if we find a match. 

For a complex type like a Student class with properties such as Id, FirstName and LastName, we can use the same technique to check for common Student names:

foreach (var student in firstList)
{
    if (secondList.Any(s =>
        s.FirstName == student.FirstName && s.LastName == student.LastName))
    {
        return true;
    }
}

return false;

How to Use Intersect to Verify the Existence of Items in Another List

Besides the use of iteration, we can leverage the use of the Intersect() method, to check whether list items exist in another list in C#. This method returns common elements in two lists, ideally performing an intersection between two lists:

return firstList.Intersect(secondList).Any();

Likewise, using our Student class example, we can invoke the Intersect() method to check whether two lists share common students:

return firstList
    .Select(student => $"{student.FirstName} {student.LastName}")
    .Intersect(secondList.Select(student => $"{student.FirstName} {student.LastName}"))
    .Any();

Check if Items of a List Exist in Another List in C# Using Any

We can use the IEnumerable.Any() method to check whether items of one list exist in another. The method works by checking whether any element of our lists exists or satisfies some conditions.

To learn more about the Any() method, check out our article Any() vs Count() in .NET: Which One is Better?

Let’s learn how we can achieve our goal with an example:

return secondList.Any(firstList.Contains);

In this case, the method returns true if secondList shares common elements with firstList. For our Student class, we can check if two List<Student> objects share common students by passing our condition while invoking the Any() method:

return secondList.Any(student =>
    firstList.Any(s =>
        s.FirstName == student.FirstName && s.LastName == student.LastName));

Verify the Existence of Items in Another List Using Except

Besides the other techniques, we can use the Except() method to check whether two lists share common elements. As the name suggests, the method retrieves elements from one list that are not in another and returns an IEnumerable<T> object.

Let’s implement an example to learn how it works:

return secondList.Except(firstList).Count() != secondList.Count;

Here, we invoke the Except() method to get the elements not in secondList. If the count of those elements equals the items in secondList, then the two lists do not share common elements. When dealing with complex types like our Student class, we can use the same strategy to check whether two List<Student> objects share the same elements:

return secondList
    .Select(student => $"{student.FirstName} {student.LastName}")
    .Except(firstList.Select(student => $"{student.FirstName} {student.LastName}"))
    .Count() != secondList.Count;

Check if Items of a List Exist in Another List Using Where in C#

We can use the Where() LINQ operator to check whether two lists share common elements as it checks for elements that meet specific criteria:

return secondList.Where(firstList.Contains).Any();

In this case, our expression combines the Where() clause with Contains() to check if secondList and firstList share common elements before returning true or false

Finally, let’s see how we can use the Where() clause to check if our List<Student> objects share common items:

return secondList
    .Any(student =>
        firstList.Any(s =>
            s.FirstName == student.FirstName && s.LastName == student.LastName));

Performance Analysis

Let’s analyze how long each method compares pairs of 1,000,000 lists. We are going to test how each method performs in different scenarios. The first scenario compares an ordered list against a reversed list, while the other compares two lists with different elements, which we populate with different numbers. Finally, the last scenario will have two lists that share a common middle element

Finally, let’s examine how each technique performs:

| Method                      |  listName | Mean             | Rank | Allocated  |
|---------------------------- |-----------|------------------|------|------------|
| CompareListUsingIteration   |  Reversed |         186.2 μs |    1 |        2 B |
| CompareListUsingAnyContains |  Reversed |         186.9 μs |    2 |      106 B |
| CompareListUsingWhereAny    |  Reversed |         187.5 μs |    3 |      138 B |
| CompareListUsingIntersect   |  Reversed |      11,964.2 μs |    4 | 18604582 B |
| CompareListUsingExcept      |  Reversed |      21,936.6 μs |    5 | 18604960 B |
| CompareListUsingIntersect   |  Middle   |      20,363.9 μs |    1 | 18606100 B |
| CompareListUsingExcept      |  Middle   |      33,473.9 μs |    2 | 57180026 B |
| CompareListUsingWhereAny    |  Middle   |  90,303,896.7 μs |    3 |   506008 B |
| CompareListUsingAnyContains |  Middle   |  92,659,048.3 μs |    4 |   523240 B |
| CompareListUsingIteration   |  Middle   |  93,294,658.7 μs |    5 |   523104 B |
| CompareListUsingIntersect   |  Exclusive|      18,955.2 μs |    1 | 18603878 B |
| CompareListUsingExcept      |  Exclusive|      33,185.8 μs |    2 | 57180763 B |
| CompareListUsingIteration   |  Exclusive| 181,602,156.8 μs |    3 |  1040064 B |
| CompareListUsingWhereAny    |  Exclusive| 181,822,515.7 μs |    4 |  1045976 B |
| CompareListUsingAnyContains |  Exclusive| 182,225,612.5 μs |    5 |  1045944 B |

Our iteration implementation performs well when comparing an ordered list and its reversed counterpart, as the solution is optimized for the common element case, breaking out of the loop as soon as a matching element is encountered. This also applies to the WhereAny() and AnyContains() methods. However, because these are O(N^2) solutions, their performance decreases as the number of uncommon elements increases.  

The Except() method performs very consistently regardless of the data, as it internally creates a HashSet<T>, leading to an O(N) solution. However, it lags behind our Intersect() method, requiring a complete pass of the excepted list to compare the final length.

The Intersect() method, also O(N), is the ideal solution in cases where we cannot know the distribution of common elements. While this solution does have an additional memory cost due to the internal creation of a HashSet<T>, this tradeoff results in higher performance in the general case.

Conclusion

In this article, we learn techniques to check if items exist in another list in C# and how they perform. Which technique did you find the most useful? Let’s discuss more in the comments section below. 

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!