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(b).Contains(x)) instead of Except().

Need the same thing in another language?

See the full reference for Python, JavaScript, SQL, Java, Go, and more.

See every language →

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#?
LINQ’s 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?
No. 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#?
Pass a custom 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?
ExceptBy (added in .NET 6) lets you compare by a key selector instead of the whole object: 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#?
There is no single built-in method, but it is two lines: 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?
For very small lists (under a few dozen items) the difference is negligible either way. The gap widens fast as lists grow: 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#?
If the class overrides 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.

Share this tool

Featured on ToolDirs
© 2026 ListDiff

Free online list and text processing tools.