前天在做批量数据导入新增时,要对数据进行有效性判断,其中还要去除重复,如果没出现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",);
List<Person> personList = new List<Person>() {
person,
new Person("XiaoMing",),
new Person("CuiYanWei",),
new Person("XiaoMing",),
new Person("XiaoMing",),
new Person("LaoWang",),
new Person("XiaoMing",),
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两个的哈希码是一样的就没有继续往下执行。

Linq之Distinct详解的更多相关文章

  1. C#中的Linq to Xml详解

    这篇文章主要介绍了C#中的Linq to Xml详解,本文给出转换步骤以及大量实例,讲解了生成xml.查询并修改xml.监听xml事件.处理xml流等内容,需要的朋友可以参考下 一.生成Xml 为了能 ...

  2. sql distinct详解以及优化

    一.distinct简介 distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用 它来返回不重复记录的条数,而不是用它来返回不重记录的所有值.其原因是distinct只有用二重循环查询 ...

  3. LINQ查询表达式详解(2)——查询表达式的转换

    简介 C#在执行LINQ查询表达式的时候,并不会指定其执行语义,而是将查询表达式转换为遵循查询表达式模式的方法的调用.具体而言,查询表达式将转换为以下名称的调用:Where.Select.Select ...

  4. LINQ查询表达式详解(1)——基本语法、使用扩展方法和Lambda表达式简化LINQ查询

    简介 使用线程的主要原因:应用程序中一些操作需要消耗一定的时间,比如对文件.数据库.网络的访问等等,而我们不希望用户一直等待到操作结束,而是在此同时可以进行一些其他的操作.  这就可以使用线程来实现. ...

  5. Code First开发系列之管理数据库创建,填充种子数据以及LINQ操作详解

    返回<8天掌握EF的Code First开发>总目录 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to ...

  6. 8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

    本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来. 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LI ...

  7. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  8. C# LINQ详解(转)

    C# LINQ详解(一)   原文标题:How does it work in C#?-Part 3 (C# LINQ in detail),作者:Mohammand A Rahman. 目录 LIN ...

  9. Linq之旅:Linq入门详解(Linq to Objects)【转】

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html Linq之旅:Linq入门详解(Linq to Objects) 示例代码下载:Linq之 ...

随机推荐

  1. ASP.NET MVC ScriptBundle 不能加载.min.js文件

    比如我用 bundles.Add(new ScriptBundle("~/bundles/easyui").Include( "~/Content/easyui/jque ...

  2. jvm lock低性能分析

    日志平台client面临着输出日志的问题.为了避免干扰业务系统,我们采用异步输出的方式.这实际上相当于一个多生产者-单消费者的多线程模型.传统的方式是使用同步加锁的方式,但是这种方式不够高效.之前 钟 ...

  3. NSUserDefaults用法详解

    一.了解NSUserDefaults以及它可以直接存储的类型 NSUserDefaults是一个单例,在整个程序中只有一个实例对象,他可以用于数据的永久保存,而且简单实用,这是它可以让数据自由传递的一 ...

  4. asp.net mvc5 分析器错误消息: 未能加载类型“XXX.MvcApplication”

    描述 今天忽然碰到一个这个错误: “/”应用程序中的服务器错误. 分析器错误 说明: 在分析向此请求提供服务所需资源时出错.请检查下列特定分析错误详细信息并适当地修改源文件. 分析器错误消息: 未能加 ...

  5. 20181015记录一个简单的TXT日志类

    20190422添加换行以及时间记录 using System; using System.Collections.Generic; using System.IO; using System.Lin ...

  6. Unity Shader 阶段性反思与总结(一)

    Unity Shader 阶段性反思与总结(一) 最近在写Shader的时候,总是感觉力不从心,感觉自己已经看了蛮久的书了,也有一定的积累了,但是一想写什么效果,完完全全就是脑袋一团空白.典型的例子就 ...

  7. 适合新手看的ref和out

    面试的时候一般很高的概率会问到ref和out的区别...我们死记硬背的话很难记住. 建议大家和我一样简单的探索一下.动手试一下就能记住了. 共同点是我们在使用ref或者out的时候一定要在写的方法里面 ...

  8. Javascript/jQuery常用方法

    //字符串转成时间 function getDate(strDate) { var date = eval('new Date(' + strDate.replace(/\d+(?=-[^-]+$)/ ...

  9. 【cocos2d-x 仙凡奇缘-网游研发(2) 角色换线系统】

    转载请注明出处:http://www.cnblogs.com/zisou/p/xianfan01.html 做一款游戏就先得制作好策划文档,和基本的人物世界构架的设计,然后架空在这样一个虚拟的世界中每 ...

  10. Regular进阶: 跨组件通信

    本文由作者郑海波授权网易云社区发布. 背景 在组件化不断深入的大环境下,无论使用哪种 MDV 框架都最终会遇到一个头疼的问题,就是「跨组件通信」. 下图是个简单的例子 这里包含「事件通信」和「数据通信 ...