使用Linq中的Distinct方法对序列进行去重操作

使用Linq提供的扩展方法Distinct可以去除序列中的重复元素。

该方法具有以下两种重载形式:

(1)public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source) (重载1)

通过使用默认的相等比较器对值进行比较并返回序列中的非重复元素。

(2)publicstatic IQueryable<TSource> Distinct<TSource>(this IQueryable<TSource> source,IEqualityComparer<TSource> comparer) (重载2)

通过使用自定义的相等比较器对值进行比较返回序列中的非重复元素。 自定义的相等比较器需要实现接口IEqualityComparer<T>。

使用默认的相等比较器时,比较器是如何工作的?

对于值类型,比较器通过比较对象的值来确认对象是否相等;

对于引用类型,比较器通过比较对象的引用来确认对象是否相等;

而对于字符串类型,尽管是引用类型,但比较器同样通过比较对象的值来确认对象是否相等。

下面给出两个使用Distinct方法去除序列重复元素的实例,其中,例1采用默认的比较器来工作,例2采用自定义的比较器工作。

例1 使用Distinct方法去除序列中的同名元素。

List<string> nameList = new List<string> { "张三", "李四", "张三", "张三", "李四"};
Console.WriteLine("原始序列:");
foreach (var s in nameList)
{
     Console.WriteLine("Name = {0}", s);
}
var newNameList = nameList.Distinct();
Console.WriteLine("去重操作后的序列:");
foreach (var s in newNameList)
{
    Console.WriteLine("Name = {0}", s);
}

例1的执行结果如下图所示。

例1中采用Distinct方法的重载1来对序列进行去重操作,即使用默认的比较器来工作,同时,序列的元素类型为字符串,所以会通过比较字符串的值来确认字符串是否相等,这样一来,在返回的结果中,重复的“张三”与“李四”将会被去除。

例2-1 使用Distinct方法去除序列中的重复的学生元素。

public class Student
{
    public string StudentName { get; set; }
    public int StudentCode { get; set; }
}
List<Student> studentList = new List<Student>();
studentList.Add(new Student() { StudentName = "张三", StudentCode = 1 });
studentList.Add(new Student() { StudentName = "李四", StudentCode = 1 });
studentList.Add(new Student() { StudentName = "张三", StudentCode = 1 });
studentList.Add(new Student() { StudentName = "张三", StudentCode = 2 });
studentList.Add(new Student() { StudentName = "李四", StudentCode = 1 });

Console.WriteLine("原始集合:");
foreach (var s in studentList)
{
    Console.WriteLine("StudentName = {0}, StudentCode = {1}", s.StudentName, s.StudentCode);
}
var newStudentList = studentList.Distinct();
Console.WriteLine("去重操作后的集合:");
foreach (var s in newStudentList)
{
    Console.WriteLine("StudentName = {0}, StudentCode = {1}", s.StudentName, s.StudentCode);
}

例2-1的执行结果如下图所示。

从结果可以看出,对序列执行Distinct方法并没有去除序列中的重复元素。因为例2-1采用的仍是Distinct方法的重载1来对序列进行去重操作,那么将会使用默认的比较器来工作,默认的比较器在比较引用类型时是通过比较对象的引用来确认对象是否相等,很显然,实例序列中的各个元素的引用是不相等的,因此比较器会认为,序列中的全部元素都不相同,因此不会进行去重操作。

例2-2 使用Distinct方法去除序列中的重复的学生元素。(该自定义的比较器出场了吧)

public class StudentComparer : IEqualityComparer<Student>
{
    public bool Equals(Student x, Student y)
    {
        if (Object.ReferenceEquals(x, y)) return true;
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
           return false;
        return x.StudentName == y.StudentName && x.StudentCode == y.StudentCode;
    }

    public int GetHashCode(Student student)
    {
        if (Object.ReferenceEquals(student, null)) return 0;
        int hashStudentName = student.StudentName == null ? 0 : student.StudentName.GetHashCode();
        int hashStudentCode = student.StudentCode.GetHashCode();
        return hashStudentName ^ hashStudentCode;
    }
}
Console.WriteLine("原始集合:");
foreach (var s in studentList)
{
    Console.WriteLine("StudentName = {0}, StudentCode = {1}", s.StudentName, s.StudentCode);
}
var newStudentList = studentList.Distinct(new StudentComparer());//将自定义的比较器作为参数传入
Console.WriteLine("去重操作后的集合:");
foreach (var s in newStudentList)
{
    Console.WriteLine("StudentName = {0}, StudentCode = {1}", s.StudentName, s.StudentCode);
}

例2-2的执行结果如下图所示。

从结果可以看出,此次成功去除了序列中的重复记录,功劳归功于我们自定义的比较器,因为自定义的比较器不再通过比较引用来确认对象是否相等了,而是通过比较Student对象的StudentName与StudentCode的值来确认对象是否相等,很显然,只要着两个字段的值相等将会被认为是重复记录而被去除,所以得到了我们想要的结果。

扩展阅读:

细说对象的相等性

使用Linq中的Distinct方法对序列进行去重操作,布布扣,bubuko.com

时间: 2024-10-11 21:49:35

使用Linq中的Distinct方法对序列进行去重操作的相关文章

LINQ中的扩展方法

LINQ中的where(),OderByDescending().Select()并不是IEnumerable<T>的方法,却能使用这些方法,查阅了资料发现是用到了C#的扩展方法. 举个小例子: 定义一个静态类StringExtension和静态方法Foo,关键字this. public static class StringExtension { public static void Foo(this string s) { Console.WriteLine("Foo invok

Python中6种内建序列之通用操作

数据结构式通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合,这些数据元素可以是数字或者字符,甚至可以是其他数据结构.在Python中,最基本的数据结构是序列(sequence).序列中的每个元素被分配一个序号--即元素的位置,也称为索引.第一个元素索引是0,第二个则是1,一次类推. Python包含6中内建的序列,即列表.元组.字符串.Unicode字符串.buffer对象和xrange对象. 通用序列操作:索引.分片.序列相加.乘法.成员资格.长度.最小值和最大值 1. 索引 序列

Linq中常用的方法

这几天闲着也是闲着,就仔细的研究了一下Linq的语法,还有他的一些扩展方法的使用. 下面是一些常用的扩展方法. Aggregate 自定义的聚合计算 All 检测序列中所有元素是否都满足指定的条件 Any 检测序列中是否存在满足指定条件的元素 Average 计算序列中元素的平均值 Cast 将序列中元素的类型转换成指定的类型(由TResult参数指定) Contact 将一个序列中的元素全部追加到另一个序列中,并构成一个新的序列. Contains 检测序列中是否存在指定的元素. Count

Linq中GroupBy方法的使用总结(转)

Group在SQL经常使用,通常是对一个字段或者多个字段分组,求其总和,均值等. Linq中的Groupby方法也有这种功能.具体实现看代码: 假设有如下的一个数据集: public class StudentScore { public int ID { set; get; } public string Name { set; get; } public string Course { set; get; } public int Score { set; get; } public str

[转]Linq中GroupBy方法的使用总结

Linq中GroupBy方法的使用总结 Group在SQL经常使用,通常是对一个字段或者多个字段分组,求其总和,均值等. Linq中的Groupby方法也有这种功能.具体实现看代码: 假设有如下的一个数据集: public class StudentScore { public int ID { set; get; } public string Name { set; get; } public string Course { set; get; } public int Score { se

扩展方法判断序列(或集合)是否包含元素

自定义扩展方法: public static class EnumerableExtensions { public static bool IsEmpty<T>(this IEnumerable<T> source) { return !source.Any(); } public static bool IsNotEmpty<T>(this IEnumerable<T> source) { return source.Any(); } } 测试用到的Pe

数据库和linq中的 join(连接)操作

sql中的连接 sql中的表连接有inner join,left join(left outer join),right join(right outer join),full join(full outer join),cross join 在此基础上我们能扩展出 left excluding join,right excluding join,full outer excluding join 注:left join是left outer join 的简写,即左连接和左外连接是一样的 首先定

Linq中Take与Skip的使用

eg:现要求查询出class_id为2的班级中年龄最大的3个学生的姓名 使用SQL语句查询时,代码如下所示. select top 3 student_name from tb_Student where class_id=2 order by student_age 在Linq中使用Take()方法结合orderby子句一起来实现 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using S

Laravel框架中的make方法详解

为什么网上已经有这么多的介绍Laravel的执行流程了,Laravel的容器详解了,Laravel的特性了,Laravel的启动过程了之类的文章,我还要来再分享呢? 因为,每个人的思维方式和方向是不一样的,所以就会出现这样的一个场景,当你遇到一个问题在网上寻求答案的时候,有很多文章都解释了你的这个问题,但是你只对其中一篇感兴趣,那是因为作者的思维方式和你的很接近而作者的文笔也可能是你喜欢的那种类型.正因如此,我也来分享一些我在研究Laravel框架时的一些观点和看法,希望给那些和我有类似思维方式