首页 > 编程语言 >C#中Linq的去重方式Distinct详解

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

时间:2024-05-10 11:33:24浏览次数:27  
标签:Name Person C# Age System Linq Distinct GetHashCode using

一、首先创建一个控制台应用程序,添加一个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);可能即使实现了接口也不了解里面是怎么个原理,我们先看下运行结果。

从上面的运行结果可以看到,两个运行结果是一样的,还是有重复的数据:例如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();
        }
    }
}

在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();
        }
    }
}

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

 

标签:Name,Person,C#,Age,System,Linq,Distinct,GetHashCode,using
From: https://www.cnblogs.com/ywtssydm/p/18183981

相关文章

  • C# 代码学习
    ......
  • 事务隔离性MVCC
    事务的隔离性是由锁和mvcc实现的。其中mvcc的意思是多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现主要是分为了三个部分,第一个是隐藏字段,第二个是undolog日志,第三个是readView读视图。隐藏字段是指:在MySQL中给每个表都设置了隐藏字段,有一个是tr......
  • react中使用craco,针对路径转换,修改webpack别名路径配置
    1.0首先下载craco依赖包npminstall@craco/craco-D2.0在项目根目录下面新建craco.config.js文件,里面内容配置为constpath=require('path')module.exports={webpack:{alias:{'@':path.resolve(__dirname,'src')}......
  • python 映射类型 dict
    dict定义字典(Dict)是一种用于存储键-值对数据的数据结构。字典使用花括号{}来创建,每个键值对之间使用冒号:分隔。可变的、无序的、key不重复。dict的特点键值对存储:字典是由键值对构成的集合,每个键值对之间使用冒号(:)分隔,键和值之间使用逗号分隔,所有键都是唯一的。无序性:字典......
  • css border-radius 如何设置不占div宽度,向外突出
    在CSS中,border-radius用于创建元素的圆角边框,但边框圆角本身是包含在元素的总宽度和高度内的,并不会额外占用外部空间或使元素尺寸变大。如果你想让圆角“向外突出”,即不占用div本身的宽度和高度,可以通过一些技巧来模拟这种效果。一种常见的方法是使用伪元素(::before和::afte......
  • WPF dynamic resources drawbacks
     Dynamic resource, on the other hand, will create a temporary expression during the initial compilation and thus defer lookup for resources until the requested resource value is actually required in order to construct an obj......
  • 204-基于Xilinx Virtex-6 XC6VLX240T 和TI DSP TMS320C6678的信号处理板
    基于XilinxVirtex-6XC6VLX240T和TIDSPTMS320C6678的信号处理板 1、板卡概述    板卡由北京太速科技自主研发,基于VPX架构,主体芯片为两片TIDSPTMS320C6678,两片Virtex-6XC6VLX240T-ff1156FPGA,1个RapidIOSwitch。FPGA连接FMC子卡。FPGA......
  • TCP长连接/HTTP长连接/HTTP长(短)轮询
     TCP长连接/HTTP长连接/HTTP长轮询TCP长连接VSHTTP长连接TCP长连接和HTTP长连接是两个相关但概念上有所区别的技术。TCP长连接TCP(TransmissionControlProtocol)是互联网传输层的一个面向连接的协议,它提供可靠的数据传输服务。在TCP连接中,长连接是指客户端和服务器建立连......
  • PHP代码运行时两个foreach迭代变量一样导致错误
    请看如下代码:$list=[1,2,4,5];$list2=[5,6,7,9];foreach($listas$key=>&$value){$value=strval($value);}foreach($list2as$key=>$value){$value=11;}pre($list);......
  • ArchLinux手动安装--适用于几乎所有linux发行版的安装方式
    注:此教程针对的是UEFI+GPT分区表的环境下进行安装,目的是安装Windows10+ArchLinux双系统,且是单硬盘安装,本人的本地环境是intel+nvidia。参考资料:https://www.youtube.com/watch?v=JRdYSGh-g3s安装前确保本地已经安装好Windows10,且为archlinux分好一定空间已经用Rufus等写盘......