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

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

前言

今天我们一起来讨论一下关于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();
            }
        }
    }
}

分析生成的报告

Method Mean Error StdDev Gen0 Gen1 Gen2 Allocated
HashSetDuplicate 7.043 ms 0.0546 ms 0.0511 ms 343.7500 343.7500 343.7500 18169.63 KB
LoopTraversalDuplicate 7.385 ms 0.0309 ms 0.0274 ms - - - 1.16 KB
DistinctDuplicate 7.034 ms 0.0497 ms 0.0465 ms 343.7500 343.7500 343.7500 18170.1 KB
GroupByDuplicate 12.685 ms 0.1025 ms 0.0958 ms 2265.6250 1781.2500 515.6250 12843.65 KB
CustomEqualityComparerDuplicate 25.608 ms 0.1826 ms 0.1708 ms 3812.5000 - - 23438.68 KB

说明:

  • 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

相关文章
|
19天前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
43 10
|
8月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
233 3
|
8月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
222 3
|
2月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
44 3
|
26天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
85 12
|
2月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
80 4
|
4月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
58 2
|
3月前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。
182 0
|
4月前
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
94 11
|
4月前
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue&lt;T&gt;`和`ConcurrentDictionary&lt;TKey, TValue&gt;`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
66 1

热门文章

最新文章