写点什么

C#集合数据去重的 5 种方式及其性能对比测试分析

作者:追逐时光者
  • 2025-10-20
    广东
  • 本文字数:4846 字

    阅读完需:约 16 分钟

前言

今天我们一起来讨论一下关于 C#集合数据去重的 5 种方式并且使用BenchmarkDotNet对这 5 种方式进行性能基准对比测试分析,每种方法都有其特点和适用场景,我们可以根据具体需求和执行效率选择一种进行使用。

BenchmarkDotNet 介绍

BenchmarkDotNet 是一个基于.NET 开源、功能全面、易于使用的性能基准测试框架,它为.NET 开发者提供了强大的性能评估和优化能力。通过自动化测试、多平台支持、高级统计分析和自定义配置等特性,BenchmarkDotNet 帮助开发者更好地理解和优化软件系统的性能表现。

使用 HashSet 去重

C# 中的 HashSet 是一种集合类型,它确保其中的元素是唯一的,不允许重复值的存在。当你尝试向 HashSet 中添加一个重复的元素时,HashSet 会忽略重复的值,而不会引发错误。这使得 HashSet 成为一个非常方便的数据结构,用于存储一组唯一的元素,并且在需要时可以高效地进行查找、插入和删除操作,注意 HashSet 中的元素是无序的。

        /// <summary>        /// 使用HashSet去重        /// TODO:HashSet是一个集合类,它的特点是不允许重复元素,可以方便地实现去重功能。        /// </summary>        public static void HashSetDuplicate()        {            var dataSource = new List<int>() { 1, 2, 3, 2, 5, 88, 99, 99, 100, 88, 30, 50, 15, 100, 99, 99, 2, 3 };            HashSet<int> uniqueData = new HashSet<int>(dataSource);
            Console.WriteLine(string.Join(", ", uniqueData));        }
复制代码

使用循环遍历去重

        /// <summary>        /// 直接循环遍历去重        /// </summary>        public static void LoopTraversalDuplicate()        {            var dataSource = new List<int>() { 1, 2, 3, 2, 5, 88, 99, 99, 100, 88, 30, 50, 15, 100, 99, 99, 2, 3 };            var uniqueData = new List<int>();            foreach (var item in dataSource)            {                //if (!uniqueData.Any(x => x == item))                //if (!uniqueData.Exists(x => x == item))                if (!uniqueData.Contains(item))                {                    uniqueData.Add(item);                }            }            Console.WriteLine(string.Join(", ", uniqueData));        }
复制代码

使用 Linq 的 Distinct()方法去重

Linq 中的 Distinct()方法用于从集合中筛选出不重复的元素。Distinct()方法基于元素的相等性来进行筛选,并返回一个包含不重复元素的新序列。底层实现还是使用到了 HashSet。

        /// <summary>        /// 使用Linq的Distinct()方法去重        /// </summary>        public static void DistinctDuplicate()        {            var dataSource = new List<int>() { 1, 2, 3, 2, 5, 88, 99, 99, 100, 88, 30, 50, 15, 100, 99, 99, 2, 3 };            var uniqueData = dataSource.Distinct().ToList();
            Console.WriteLine(string.Join(", ", uniqueData));        }
复制代码

使用 Linq 的 GroupBy()方法去重

GroupBy()方法将原始集合中的元素进行分组,根据指定的键或条件进行分组。每个分组都会有一个唯一的键,通过将原始集合分组并选择每个分组中的第一个元素,实现了去重的效果。

        /// <summary>        /// 使用Linq的GroupBy()方法去重        /// </summary>        public static void GroupByDuplicate()        {            var dataSource = new List<int>() { 1, 2, 3, 2, 5, 88, 99, 99, 100, 88, 30, 50, 15, 100, 99, 99, 2, 3 };
            //GroupBy()方法将原始集合中的元素进行分组,根据指定的键或条件进行分组。每个分组都会有一个唯一的键,通过将原始集合分组并选择每个分组中的第一个元素,实现了去重的效果。            var uniqueData = dataSource.GroupBy(item => item).Select(group => group.First()).ToList();
            Console.WriteLine(string.Join(", ", uniqueData));        }
复制代码

使用自定义的比较器和循环遍历

        /// <summary>        /// 使用自定义的比较器和循环遍历        /// </summary>        public static void CustomEqualityComparerDuplicate()        {            var dataSource = new List<int>() { 1, 2, 3, 2, 5, 88, 99, 99, 100, 88, 30, 50, 15, 100, 99, 99, 2, 3 };            var uniqueData = new List<int>();            foreach (var item in dataSource)            {                if (!uniqueData.Contains(item, new CustomEqualityComparer()))                {                    uniqueData.Add(item);                }            }            Console.WriteLine(string.Join(", ", uniqueData));        }
        /// <summary>        /// 自定义的比较器        /// </summary>        public class CustomEqualityComparer : IEqualityComparer<int>        {            public bool Equals(int x, int y)            {                return x == y;            }
            public int GetHashCode(int obj)            {                return obj.GetHashCode();            }        }
复制代码

性能基准对比测试分析

接下来我们使用BenchmarkDotNet对这 5 种集合去重的方式进行性能基准对比测试分析。

测试代码

using BenchmarkDotNet.Attributes;
namespace BenchmarkDotNetExercise{    [MemoryDiagnoser]//记录内存分配情况    public class DataSetDeduplicationBenchmark    {        private List<int> dataSource;
        public DataSetDeduplicationBenchmark()        {            // 生成大量重复数据              dataSource = Enumerable.Repeat(Enumerable.Range(1, 100), 10000).SelectMany(x => x).ToList();        }
        /// <summary>        /// 使用HashSet去重        /// TODO:HashSet是一个集合类,它的特点是不允许重复元素,可以方便地实现去重功能。        /// </summary>        [Benchmark]        public void HashSetDuplicate()        {            HashSet<int> uniqueData = new HashSet<int>(dataSource);        }
        /// <summary>        /// 直接循环遍历去重        /// </summary>        [Benchmark]        public void LoopTraversalDuplicate()        {            var uniqueData = new List<int>();            foreach (var item in dataSource)            {                //if (!uniqueData.Any(x => x == item))                //if (!uniqueData.Exists(x => x == item))                if (!uniqueData.Contains(item))                {                    uniqueData.Add(item);                }            }        }
        /// <summary>        /// 使用Linq的Distinct()方法去重        /// </summary>        [Benchmark]        public void DistinctDuplicate()        {            var uniqueData = dataSource.Distinct().ToList();        }
        /// <summary>        /// 使用Linq的GroupBy()方法去重        /// </summary>        [Benchmark]        public void GroupByDuplicate()        {            //GroupBy()方法将原始集合中的元素进行分组,根据指定的键或条件进行分组。每个分组都会有一个唯一的键,通过将原始集合分组并选择每个分组中的第一个元素,实现了去重的效果。            var uniqueData = dataSource.GroupBy(item => item).Select(group => group.First()).ToList();        }
        /// <summary>        /// 使用自定义的比较器和循环遍历        /// </summary>        [Benchmark]        public void CustomEqualityComparerDuplicate()        {            var uniqueData = new List<int>();            foreach (var item in dataSource)            {                if (!uniqueData.Contains(item, new CustomEqualityComparer()))                {                    uniqueData.Add(item);                }            }        }
        /// <summary>        /// 自定义的比较器        /// </summary>        public class CustomEqualityComparer : IEqualityComparer<int>        {            public bool Equals(int x, int y)            {                return x == y;            }
            public int GetHashCode(int obj)            {                return obj.GetHashCode();            }        }    }}
复制代码

分析生成的报告


说明:

  • Mean: 所有测量值的算术平均值。

  • Error: 99.9% 置信区间的一半。

  • StdDev: 所有测量值的标准差。

  • Gen0: 第 0 代 GC 每 1000 次操作收集一次。

  • Gen1: 第 1 代 GC 每 1000 次操作收集一次。

  • Gen2: 第 2 代 GC 每 1000 次操作收集一次。

  • Allocated: 每次操作分配的内存(仅托管内存,包含所有内容,1KB = 1024B)。

  • 1 ms: 1 毫秒(0.001 秒)。

C#/.NET 编程技巧练习集

本文已加入 C#/.NET 编程技巧练习集:https://github.com/YSGStudyHards/DotNetExercises


发布于: 刚刚阅读数: 3
用户头像

不积跬步无以至千里,不积小流无以成江海! 2020-01-14 加入

微软MVP、华为云HCDE、华为云云享专家、51CTO专家博主、阿里云专家博主、博客园推荐博客、CSDN博客专家、腾讯云创作之星、掘金优秀创作者,一个热爱开源的全栈软件工程师,擅长C#、.NET、Vue等相关技术开发。

评论

发布
暂无评论
C#集合数据去重的5种方式及其性能对比测试分析_C#_追逐时光者_InfoQ写作社区