C#中Linq的去重方式Distinct详解

作者:社会主义接班人 时间:2022-01-23 03:53:36 

前天在做批量数据导入新增时,要对数据进行有效性判断,其中还要去除重复,如果没出现linq的话可能会新声明一个临时对象集合,然后遍历原始数据判断把符合条件的数据添加到临时集合中,这在有了linq之后显得比较麻烦。

一、首先创建一个控制台应用程序,添加一个Person对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Compare
{
   public class Person
   {
       public string Name { get; set; }

public int Age { get; set; }

public Person(string name, int age)
       {
           this.Name = name;
           this.Age = age;
       }
   }
}

二、创建测试数据

创建了一个Name="ZhangSan"的Person对象,放入personList两次,然后personList又创建了几个Person对象,这几个Person对象中也有Name、Age都重复的。例如:"XiaoMing",26.

Person person = new Person("ZhangSan",26);
           List<Person> personList = new List<Person>() {
               person,
               new Person("XiaoMing",25),
               new Person("CuiYanWei",25),
               new Person("XiaoMing",26),
                new Person("XiaoMing",25),
               new Person("LaoWang",26),
               new Person("XiaoMing",26),
               person
           };

三、测试

下面的代码中用了两种方式来选择不重复的数据。

List<Person> defaultDistinctPersons = personList.Distinct().ToList<Person>();
           foreach (Person p in defaultDistinctPersons)
           {
               Console.WriteLine("Name:{0}    Age:{1}",p.Name,p.Age);
           }
           Console.WriteLine("-----------------------------------------------------");
           List<Person> comparePersons = personList.Distinct(new PersonCompare()).ToList<Person>();
           foreach (Person p in comparePersons)
           {
               Console.WriteLine("Name:{0}    Age:{1}", p.Name, p.Age);
           }
           Console.ReadLine();

在华丽分割线上面是使用默认的distinct,下面是通过集成IEqualityComparer接口。下面是实现接口的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Compare
{
   public class PersonCompare:IEqualityComparer<Person>
   {
       public bool Equals(Person x, Person y)
       {
           if (x == null || y == null)
               return false;
           return x.Name.Equals(y.Name) && x.Age == y.Age;
       }
       public int GetHashCode(Person obj)
       {
           return obj.GetHashCode();
       }
   }
}

在上面的代码中,继承IEqualityComparer接口,主要是实现了两个方法:bool Equals(T x, T y);int GetHashCode(T obj);可能即使实现了接口也不了解里面是怎么个原理,我们先看下运行结果。

C#中Linq的去重方式Distinct详解

从上面的运行结果可以看到,两个运行结果是一样的,还是有重复的数据:例如XiaoMing,26.两个都没去除重复,只有ZhangSan那两个去除重复了。是不是有实现接口多此一举的感觉。那为什么还要有这个接口还要实现它呢?其实要说下GetHashCode和Equals。

在说GetHashCode和Equals之前先了解下distinct(),这个方法Distinct 默认比较的是对象的引用,所以使用默认的distinct()方法是ZhangSan对象是过滤除去的,而XiaoMing,26是两个不同的对象,没有除去。

然后说下GetHashCode和Equals两个方法.

1.哈希码哈希代码是一个用于在相等测试过程中标识对象的数值。它还可以作为一个集合中的对象的索引。如果两个对象的 Equals 比较结果相等,则每个对象的 GetHashCode 方法都必须返回同一个值。 如果两个对象的比较结果不相等,这两个对象的 GetHashCode 方法不一定返回不同的值.
简而言之,如果你发现两个对象 GetHashCode() 的返回值相等,那么这两个对象就很可能是同一个对象;但如果返回值不相等,这两个对象一定不是同一个对象.

当GetHashCode可以直接分辨出不相等时,Equals就没必要调用了,而当GetHashCode返回相同结果时,Equals方法会被调用从而确保判断对象是否真的相等。所以,还是那句话:GetHashCode没必要一定把对象分辨得很清楚(况且它也不可能,一个int不可能代表所有的可能出现的值),有Equals在后面做保障。GetHashCode仅需要对对象进行快速判断。

上面的几句算是总结性的说明了两个方法的是怎么个路子,这也能解释出ZhangSan的重复去除,而其他的几个对象没有去重复的原因,ZhangSan那是一个对象,其他的虽然Name、Age相等,但不是同一个对象。

我们可以稍微改动下代码来验证上面的语句.在实现IEqualityComparer的接口类中打印出一些信息就能看明白

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Compare
{
   public class PersonCompare:IEqualityComparer<Person>
   {
       public bool Equals(Person x, Person y)
       {
           if (x == null || y == null)
               return false;
           Console.WriteLine("XName:{0} XAge:{1} XHashCode:{2}  YName:{3} YAge:{4} YHashCode:{5}", x.Name, x.Age, x.GetHashCode(),y.Name,y.Age,y.GetHashCode());
           return x.Name.Equals(y.Name) && x.Age == y.Age;
       }
       public int GetHashCode(Person obj)
       {
           Console.WriteLine("GetHashCode Name:{0} Age:{1} HashCode:{2}",obj.Name,obj.Age,obj.GetHashCode());
           return obj.GetHashCode();
       }
   }
}

C#中Linq的去重方式Distinct详解

在GetHashCode中打印了对象的Name、Age和HashCode。可以看到HashCode只有ZhangSan的是相同的,在Equals方法中只打印出了ZhangSan的,还是因为上面的先判断HashCode,相等了再使用Equals判断。

我们再改动下实现IEqualityComparer的接口类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Compare
{
   public class PersonCompare:IEqualityComparer<Person>
   {
       public bool Equals(Person x, Person y)
       {
           if (x == null || y == null)
               return false;
           Console.WriteLine("XName:{0} XAge:{1} XHashCode:{2}  YName:{3} YAge:{4} YHashCode:{5}", x.Name, x.Age, x.GetHashCode(), y.Name, y.Age, y.GetHashCode());
           return x.Name.Equals(y.Name) && x.Age == y.Age;
       }
       public int GetHashCode(Person obj)
       {
           //Console.WriteLine("GetHashCode Name:{0} Age:{1} HashCode:{2}",obj.Name,obj.Age,obj.GetHashCode());
           //return obj.GetHashCode();
           string s = string.Format("{0}_{1}",obj.Name,obj.Age);
           Console.WriteLine("Name:{0} Age:{1} HashCode:{2}",obj.Name,obj.Age, s.GetHashCode());
           return s.GetHashCode();
       }
   }
}

C#中Linq的去重方式Distinct详解

根据上面的的代码和测试结果我们可以看到,GetHashCode执行了7次(7个对象),Equals执行了3次,因为ZhangSan,26和XiaoMing,25两个的哈希码是一样的就没有继续往下执行。

来源:https://www.cnblogs.com/5ishare/p/5730337.html

标签:C#,Linq,去重,方式,Distinct
0
投稿

猜你喜欢

  • java使用JNA(Java Native Access)调用dll的方法

    2022-02-06 09:27:34
  • C#实现提取Word中插入的多媒体文件(视频,音频)

    2022-09-06 14:45:20
  • C#判断一天、一年已经过了百分之多少的方法

    2022-07-16 15:23:11
  • C#验证身份证号码正确性的实例代码(收藏)

    2023-12-18 17:28:33
  • Android Studio 3.0中mipmap-anydpi-v26是什么东东

    2023-10-11 01:17:44
  • Java设计模式之访问者模式使用场景及代码示例

    2021-06-27 13:34:25
  • 对Java ArrayList的自动扩容机制示例讲解

    2022-04-09 07:58:25
  • java实现动态 代理方法浅析

    2023-11-28 23:33:59
  • Java 中的Printstream介绍_动力节点Java学院整理

    2021-12-21 06:08:36
  • SpringBoot使用SchedulingConfigurer实现多个定时任务多机器部署问题(推荐)

    2021-09-17 07:19:20
  • Java JDK 二分法 分析demo(推荐)

    2022-02-28 23:29:21
  • springboot使用mybatis一对多的关联查询问题记录

    2023-05-25 14:31:03
  • C#实现学生成绩管理系统

    2021-06-05 22:51:31
  • Android仿微信发送语音消息的功能及示例代码

    2021-07-06 19:31:39
  • 深入学习C语言中的函数指针和左右法则

    2022-07-22 03:44:41
  • Java使用TCP实现在线聊天的示例代码

    2021-10-16 23:49:53
  • 如何用Java注解和反射实现依赖注入

    2022-11-04 11:34:10
  • Android开发ImageView图片无法显示解决过程

    2023-06-07 21:34:39
  • IDEA版最新MyBatis程序配置教程详解

    2022-08-25 03:27:26
  • unity 实现摄像机绕某点旋转一周

    2021-06-11 16:48:57
  • asp之家 软件编程 m.aspxhome.com