Keywords: C# | List<T> | Lambda Expressions | Distinct() | Deduplication
Abstract: This article explores the optimal methods for removing duplicate values from List<T> in C# using lambda expressions. By analyzing the LINQ Distinct() method and its underlying implementation, it explains how to preserve original order, handle complex types, and balance performance with memory usage. The article also compares scenarios involving new list creation versus modifying existing lists, and provides the DistinctBy() extension method for custom deduplication logic.
Introduction
In C# programming, removing duplicate elements from collections is a common task. Particularly when working with List<T>, efficient deduplication is crucial for performance optimization. This article will use a specific example as a basis to discuss best practices for implementing deduplication using lambda expressions and LINQ methods.
Problem Context
Assume we have a list of type List<long>: List<long> longs = new List<long> { 1, 2, 3, 4, 3, 2, 5 };. The goal is to remove duplicate values to obtain the result {1, 2, 3, 4, 5}, while keeping the code as concise and efficient as possible.
Core Solution: The Distinct() Method
The simplest and most efficient approach is to use the LINQ Distinct() extension method. For the example above, it can be implemented as: List<long> unique = longs.Distinct().ToList();. This line of code first calls the Distinct() method to remove duplicate elements, then converts the result to a new list using ToList().
The Distinct() method returns an IEnumerable<T> sequence that uses a hash table (HashSet<T>) to track seen elements, ensuring each element appears only once. Although the official documentation does not explicitly guarantee order preservation, the current implementation returns elements in the order of their first occurrence, which is sufficient for most applications.
Performance and Memory Considerations
Using Distinct().ToList() creates a new list, leaving the original list unchanged. This method has a time complexity of O(n) and a space complexity of O(n), as it needs to store the deduplicated result. If modifying the original list is not required, this is the recommended approach.
If avoiding the creation of a new list is desired, one can directly manipulate the existing list, but this is often more complex and error-prone. For example, loops and the RemoveAll() method can be used, but careful index management is necessary.
Deferred Execution and Iteration
If immediate conversion to a list is not needed, the result can be kept as IEnumerable<long> unique = longs.Distinct();. This leverages LINQ's deferred execution, performing deduplication only during iteration. However, note that each iteration will re-execute the deduplication logic, which may impact performance.
Extension Method: DistinctBy()
For lists containing complex types, deduplication based on a specific property may be required. In such cases, a custom DistinctBy() extension method can be used. For example: IEnumerable<Foo> distinctList = sourceList.DistinctBy(x => x.FooName);.
Here is the implementation code for DistinctBy(): public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { var knownKeys = new HashSet<TKey>(); return source.Where(element => knownKeys.Add(keySelector(element))); }. This method uses a HashSet<TKey> to track keys, ensuring each key appears only once.
Practical Recommendations
When choosing a deduplication method, consider the following factors: whether the original list should remain unchanged, the importance of order, data volume, and performance requirements. For most scenarios, Distinct().ToList() is the best choice, as it balances simplicity, performance, and readability.
If dealing with large datasets or frequent deduplication, consider using HashSet<T> directly to store unique values, though this may not be suitable for cases requiring list-specific features.
Conclusion
Using lambda expressions and the LINQ Distinct() method, duplicate values can be efficiently and concisely removed from List<T>. This article has detailed its implementation principles, performance characteristics, and extended applications, helping developers select the most appropriate solution based on specific needs.