前言

关于C#中默认的Distinct方法在什么情况下才能去重,这个就不用我再多讲,针对集合对象去重默认实现将不再满足,于是乎我们需要自定义实现来解决这个问题,接下来我们详细讲解几种常见去重方案,孰好孰歹自行判之。

首先给出我们需要用到的对象,如下:

public class Person
{public string Name { get; set; }public int Age { get; set; }
}

接下来我们添加100万条数据到集合中,如下:

            var list = new List<Person>();for (int i = 0; i < 1000000; i++){list.Add(new Person() { Age = 18, Name = "迷恋自留地" });}for (int i = 0; i < 1000; i++){list.Add(new Person() { Age = 19, Name = "迷恋自留地" });}

第一种分组去重

年龄和名称进行分组,然后取第一条即可达到去重,如下:

var  list1 = list.GroupBy(d => new { d.Age, d.Name }).Select(d => d.FirstOrDefault()).ToList();

第二种 HashSet去重 (扩展方法)

C#中HashSet对于重复元素会进行过滤筛选,所以我们写下如下扩展方法(在静态函数中定义),遍历集合元素,最后利用HashSet进行过滤达到去重目的,如下:

public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector){var hashSet = new HashSet<TKey>();foreach (TSource element in source){if (hashSet.Add(keySelector(element))){yield return element;}}}

述扩展方法即可去重,如下:

 var  list2 = list.Distinct(d => new { d.Age, d.Name }).ToList();

第三种 IEqualityComparer去重 (扩展方法)

在实际项目中有很多通过具体实现类实现该接口,通过重写Equals和HashCode比较属性值来达到去重目的,因为对于每一个类都得实现对应比较器,所以并不通用,反而利用上述方式才是最佳,其实我们大可借助该比较接口实现通用解决方案,对于每一个类都得实现一个比较器的原因在于,我们将属性比较放在类该接口内部,如果我们将属性比较放在外围呢,这个时候就达到了通用解决方案,那么我们怎么实现呢,通过委托来实现,实现该接口的本质无非就是比较HashCode,然后通过Equals比较其值,当比较HashCode时,我们强制其值为一个常量(比如0),当重写Equals方法我们调用委托即可,如下

public static class Extensions
{public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer)where T : class=> source.Distinct(new DynamicEqualityComparer<T>(comparer));private sealed class DynamicEqualityComparer<T> : IEqualityComparer<T>where T : class{private readonly Func<T, T, bool> _func;public DynamicEqualityComparer(Func<T, T, bool> func){_func = func;}public bool Equals(T x, T y) => _func(x, y);public int GetHashCode(T obj) => 0;}
}

最终通过指定属性进行比较即可去重,如下:

list = list.Distinct((a, b) => a.Age == b.Age && a.Name == b.Name).ToList();

性能比较

以上3种常见方式我们已经介绍完毕了,当数据量比较小时,我们大可忽略对集合进行各种操作所带来的性能,但是一旦数据量很大时,我们可能需要考虑性能,能节省一点时间或许有必要,于是乎,在上述100万条数据前提下,我们来分析其耗时情况,如下:

var list = new List<Person>();
for (int i = 0; i < 1000000; i++)
{list.Add(new Person() { Age = 18, Name = "jeffcky" });
}var time1 = Time(() =>
{list.GroupBy(d => new { d.Age, d.Name }).Select(d => d.FirstOrDefault()).ToList();
});
Console.WriteLine($"分组耗时:{time1}");var time2 = Time(() =>
{list.Distinct(d => new { d.Age, d.Name }).ToList();
});
Console.WriteLine($"HashSet耗时:{time2}");var time3 = Time(() =>
{list.Distinct((a, b) => a.Age == b.Age && a.Name == b.Name).ToList();
});
Console.WriteLine($"委托耗时:{time3}");static long Time(Action action)
{var stopwatch = new Stopwatch();stopwatch.Start();action();stopwatch.Stop();return stopwatch.ElapsedMilliseconds;
}

参考:https://www.cnblogs.com/CreateMyself/p/12863407.html

C# Linq 的三种去重方式(Distinct)相关推荐

  1. 三种去重方式——HashSet、Redis去重、布隆过滤器(BloomFilter)

    三种去重方式 去重就有三种实现方式,那有什么不同呢? HashSet 使用java中的HashSet不能重复的特点去重.优点是容易理解.使用方便. 缺点:占用内存大,性能较低. Redis去重 使用R ...

  2. hive 三种去重方式

    在hive数据清洗这里总结三种常用的去重方式 1.distinct 2.group by 3.row_number() 实例: SELECT tel, link_name, certificate_n ...

  3. hive安装测试及Hive 元数据的三种存储方式

    一  hive安装测试 1.下载解压 tar -xf hive-x.y.z.tar.gz(本次安装为hive-0.8.1.tar.gz) 将解压后的hive-0.8.1文件放在系统的/home/had ...

  4. (转)EF三种编程方式详细图文教程(C#+EF)之Database First

    Entity Framework4.1之前EF支持"Database First"和"Model First"编程方式,从EF4.1开始EF开始支持支持&quo ...

  5. Oracle中的四种去重方式

    create table test(id int primary key not null,name varchar(10) not null,age int not null );insert in ...

  6. java list初始化数据_Java中初始化List的5种方法 /List的2种去重方式

    前言 List是java重要的数据结构之一,我们经常接触到的有ArrayList.Vector和LinkedList三种,他们都继承来自java.util.Collection接口,类图如下: 1.构 ...

  7. Java基础知识 21(Set集合,HashSet集合以及它的三种遍历方式(迭代器,增强for循环,forEach),LinkedHashSet集合,TreeSet集合(自然排序法,比较器排序法))

    Java基础知识 21 Set集合 Set集合:一个不包含重复元素的Collection集合,元素不重复,List集合是允许元素重复的. Set接口的三个字类:HashSet(),LinkedHash ...

  8. Hive metastore三种配置方式

    Hive的meta数据支持以下三种存储方式,其中两种属于本地存储,一种为远端存储.远端存储比较适合生产环境.Hive官方wiki详细介绍了这三种方式,链接为:Hive Metastore. 一.本地d ...

  9. python数据结构与算法:二叉树及三种遍历方式(先序遍历/中序遍历/后序遍历)

    树的实现采用queue的形式: 树的三种遍历方式(广度优先白能力法):先序遍历(根左右),中序遍历(左根右)以及后序遍历(左右根) ######################P6.4 数据结构### ...

最新文章

  1. python文件粉碎传输_python使用stuck 实现scoket编程实现文件传输
  2. oracle安装实训心得,oracle数据库实训心得.docx
  3. solver.prototxt文件里面参数含义及其设置
  4. 面向对象的程序设计在游戏开发中使用(一):类
  5. 文件从一台服务器拷贝到另一台服务器
  6. 目标检测-20种常用深度学习算法论文、复现代码汇总
  7. CentOS安装l2tpd
  8. 完全公平调度 c语言,使用完全公平调度程序(CFS)进行多任务处理
  9. [内核内存] [arm64] 内存初始化4---bootm_init
  10. 安装vs2015 、如何创建MFC项目
  11. 智能插帧,打造丝滑视频体验
  12. element rules不生效
  13. JAVA使用接口实现类的功能------JAVA入门基础教程
  14. Dump文件介绍与使用
  15. C语言中的指数函数pow()问题
  16. 小妲己智能机器人要连接wifi吗_腾讯智能机器人妲己上线 万千宅男的梦即将实现...
  17. win7 桌面图标显示不正常
  18. mysql 事件跟踪_ORACLE 事件跟踪
  19. 为什么win10默认浏览器更改不了已解决
  20. 给赞!移动端网页调试利器-uc开发者工具

热门文章

  1. Java新手小白入门篇 项目 - 深海杀手
  2. CF1326E Bombs(思维题)
  3. 码元和码点(字符串截取截取得是码元,汉字有一些偏僻字占据两个码元,字符的函数往往不是我们期待)
  4. 【正点原子FPGA连载】第六章Petalinux设计流程实战摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南
  5. BP神经网络实例及代码分析(python+tensorflow实现)
  6. 将进酒翻译软件测试,乐府诗《将进酒》拼音及翻译整理
  7. 计算机应用基础与实训教程word2003文字处理软件 教学目标,计算机基础教学计划多篇...
  8. Franka Emika Panda连接真实机械臂(一)
  9. 网络通信学习笔记之 ———Socket网络通信
  10. ubuntu更换源(清华、中科大、阿里)