How to Find the Difference Between Two Lists in C#
LINQ's Except() handles most list-difference needs in C# in one line, but it behaves like a set operation - it quietly drops duplicates and doesn't preserve order. Here are the four approaches worth knowing, and when to reach for something other than Except().
Quick answer
var aOnly = a.Except(b).ToList(); is the standard one-liner - it runs in roughly O(n + m) but removes duplicates and doesn't preserve order. For symmetric difference (items in exactly one list), chain a.Except(b).Concat(b.Except(a)). In .NET 6+, use ExceptBy(b.Select(x => x.Key), x => x.Key) to compare objects by a key instead of full equality. To keep duplicates, use a.Where(x => !new HashSet instead of Except().
Need the same thing in another language?
See the full reference for Python, JavaScript, SQL, Java, Go, and more.
Except(): the standard LINQ approach
var a = new List<string> { "apple", "banana", "cherry", "banana" };
var b = new List<string> { "banana", "date" };
var aOnly = a.Except(b).ToList();
Console.WriteLine(string.Join(", ", aOnly));
// apple, cherry - order not guaranteed, duplicate "banana" already excluded and dropped Except() builds a hash set from b internally, so it runs in roughly O(n + m) rather than comparing every item against every other item. The catch: it treats a as a set too, so any duplicate values in a collapse to a single occurrence in the result, and the original order isn't guaranteed to be preserved across .NET versions.
Case-insensitive difference with a custom comparer
var a = new List<string> { "Apple", "Banana", "Cherry" };
var b = new List<string> { "banana", "date" };
var aOnly = a.Except(b, StringComparer.OrdinalIgnoreCase).ToList();
Console.WriteLine(string.Join(", ", aOnly));
// Apple, Cherry Except() accepts an optional IEqualityComparer<T> as its second argument. StringComparer.OrdinalIgnoreCase is the built-in one for case-insensitive string comparison - no need to lowercase either list yourself. For custom types, implement your own IEqualityComparer<T> the same way.
ExceptBy: comparing by a key (.NET 6+)
record Person(int Id, string Name);
var peopleA = new List<Person> { new(1, "Alice"), new(2, "Bob"), new(3, "Cara") };
var peopleB = new List<Person> { new(2, "Bob (updated)") };
var aOnly = peopleA.ExceptBy(peopleB.Select(p => p.Id), p => p.Id).ToList();
// Alice, Cara - matched on Id, not full record equality ExceptBy lets you compare on a single key without overriding equality on the whole object, and without projecting both lists down to plain key lists yourself first. Before .NET 6, the usual workaround was peopleA.Where(p => !peopleB.Select(x => x.Id).Contains(p.Id)), which works but re-scans peopleB for every item in peopleA unless you materialize the key list into a HashSet<int> first.
Preserving duplicates with HashSet + Where
var a = new List<string> { "apple", "banana", "cherry", "banana" };
var b = new List<string> { "banana", "date" };
var setB = new HashSet<string>(b);
var aOnly = a.Where(x => !setB.Contains(x)).ToList();
Console.WriteLine(string.Join(", ", aOnly));
// apple, cherry - order preserved, and a repeated non-matching item would appear twice Converting b to a HashSet<string> first keeps the lookup O(1) per item, so the whole filter stays O(n + m) while preserving both the original order of a and any duplicate values that aren't in b. Skipping the HashSet and calling b.Contains(x) directly on the list works identically but degrades to O(n * m).
Just need the answer, not the code? Paste both lists.
Open Compare 2 Lists →Frequently Asked Questions
What is the standard way to find the difference between two lists in C#?
Except(): var aOnly = a.Except(b).ToList();. It runs in roughly O(n + m) using a hash-based lookup internally, but it is a set operation - it removes duplicates from the result and does not preserve the original order of a.Does Except() preserve duplicates?
Except() treats its input as a set, so if a contains the same value twice, it appears at most once in the output - even if that value isn’t in b at all. If you need duplicates preserved, use Where(x => !b.Contains(x)) instead, ideally with b converted to a HashSet<T> first for speed.How do I do a case-insensitive list difference in C#?
IEqualityComparer<string> as the second argument to Except(): a.Except(b, StringComparer.OrdinalIgnoreCase). This compares values case-insensitively without needing to lowercase either list first.What is ExceptBy and when should I use it?
peopleA.ExceptBy(peopleB.Select(p => p.Id), p => p.Id) finds people in peopleA whose Id doesn’t appear in peopleB. Before .NET 6, the workaround was Except() combined with a custom IEqualityComparer or comparing plain key lists directly.How do I get symmetric difference in C#?
var symDiff = a.Except(b).Concat(b.Except(a)).ToList();, or using a HashSet<T>: build one set, call SymmetricExceptWith(b) on it, which mutates the set in place to contain only items in exactly one of the two collections.Is Except() slower than a nested loop for small lists?
Except() builds a hash set internally and runs in roughly O(n + m), while a nested Where(x => !b.Contains(x)) without pre-hashing b is O(n * m) and becomes noticeably slower once either list passes a few thousand items.How do I find the difference between two lists of objects, not strings, in C#?
Equals and GetHashCode consistently, Except() works directly. Otherwise, either implement IEquatable<T> on the class, pass a custom IEqualityComparer<T>, or in .NET 6+ use ExceptBy with a key selector so you don't need to override equality on the whole object at all.