using System.Collections.Generic;
using System.Diagnostics;
namespace LinqRemovalBenchmark
public string Company { get; set; }
public string SKU { get; set; }
static void Main(string[] args)
var originalA = new List<MyObject>(N);
var originalB = new List<MyObject>(N);
for (int i = 0; i < N; i++)
originalA.Add(new MyObject { Company = "CompanyA", SKU = $"SKU{i}" });
originalB.Add(new MyObject { Company = "CompanyB", SKU = $"SKU{i + (N/2)}" });
Time(nameof(RemoveAllWithHashSet),
() => RemoveAllWithHashSet(new List<MyObject>(originalA), originalB));
Time(nameof(WhereWithHashSet),
() => WhereWithHashSet(new List<MyObject>(originalA), originalB));
Time(nameof(ExceptByLinq),
() => ExceptByLinq(new List<MyObject>(originalA), originalB));
static void RemoveAllWithHashSet(List<MyObject> listA, List<MyObject> b)
var bSkuSet = new HashSet<string>(b.Select(x => x.SKU));
listA.RemoveAll(x => bSkuSet.Contains(x.SKU));
Console.WriteLine($" Remaining after RemoveAll: {listA.Count}");
static void WhereWithHashSet(List<MyObject> listA, List<MyObject> b)
var bSkuSet = new HashSet<string>(b.Select(x => x.SKU));
var filtered = listA.Where(x => !bSkuSet.Contains(x.SKU)).ToList();
Console.WriteLine($" Remaining after Where: {filtered.Count}");
static void ExceptByLinq(List<MyObject> listA, List<MyObject> b)
.ExceptBy(b.Select(x => x.SKU), x => x.SKU)
Console.WriteLine($" Remaining after ExceptBy: {result.Count}");
static void Time(string label, Action action)
GC.WaitForPendingFinalizers();
var sw = Stopwatch.StartNew();
Console.WriteLine($"{label} took {sw.Elapsed.TotalMilliseconds:N0} ms");
Console.WriteLine(new string('-', 40));