利用Distinct()内置方法对List集合的去重问题详解
前言
说到对集合去重处理,第一时间想到的肯定是Linq的Distinct扩展方式,对于一般的值类型集合去重,很好处理,直接list.Distinct()即可。但是如果想要对一个引用类型的集合去重(属性值都相同就认为重复),就会发现,直接Distinct()是不行的
先来看看泛型链表List
publicclassList
:IList ,ICollection ,IList,ICollection,IReadOnlyList ,IReadOnlyCollection ,IEnumerable ,IEnumerable
可见它实现了IEnumerable
使用这个方法时要注意:
(1)该方法并不会改变原来的链表;
(2)该方法返回一个对象(假设叫做dis),通过该对象可以枚举原链表中的非重复元素,但是并没有把非重复元素复制一份到新的对象中(连签拷贝也没有)
(3)由于(2),在枚举dis时,始终是依赖于原有链表,所以如果在获得dis后,又更新了原有链表,那么使用dis枚举将会使用原有链表的最新状态。
varlist=newList()///表明具有重复值得集合
有时候Distinct()不能对引用类型去重时我们就要自定义了自定义代码如下:
publicclassUser { publicintId{get;set;} publicstringName{get;set;} } varlist=newList() { newUser(){Id=1,Name="张三"}, newUser(){Id=1,Name="张三"}, newUser(){Id=3,Name="李四"}, }; varnewList1=list.Distinct().ToList();
运行上述代码会发现,并不是预期想要的结果,newList1还是有3个元素。之所以会产生这样的结果,是因为Distinct()是通过使用默认的相等比较器对值进行比较返回序列中的非重复元素。对于值类型,默认的相等比较器是比较值是否相等,对于引用类型,默认的相等比较器是比较对象的引用地址,所以上述例子中即使属性值都相同,也不能去重。
IEqualityComparer
聪明的我们,很容易就能发现,Linq已经为我们重载了一个去重方法,可以满足我们的需求:
publicstaticIEnumerableDistinct (thisIEnumerable source,IEqualityComparer comparer);
重载的这个方法,多提供了一个参数IEqualityComparer
publicclassUserComparer:IEqualityComparer{ publicboolEquals(Userx,Usery) { returnx.Id==y.Id&&x.Name==y.Name; } publicintGetHashCode(Userobj) { returnobj.ToString().GetHashCode(); } }
IEqualityComparer
arnewList2=list.Distinct(newUserComparer()).ToList();
甚至我们还可以实现只要某个属性相同就认为重复的效果,只需要在Equals方法按想要比较方式进行处理即可
延伸思考
Distinct的重载方法,基本已经能够满足我们的各式各样的去重需求了,但是想来想去,还是觉得有点别扭,那就是如果有类似的去重需求,我们都要新增一个类去实现IEqualityComparer
publicclassLambdaComparer:IEqualityComparer { privatereadonlyFunc _lambdaComparer; privatereadonlyFunc _lambdaHash; publicLambdaComparer(Func lambdaComparer) :this(lambdaComparer,EqualityComparer .Default.GetHashCode) { } publicLambdaComparer(Func lambdaComparer,Func lambdaHash) { if(lambdaComparer==null) thrownewArgumentNullException("lambdaComparer"); if(lambdaHash==null) thrownewArgumentNullException("lambdaHash"); _lambdaComparer=lambdaComparer; _lambdaHash=lambdaHash; } publicboolEquals(Tx,Ty) { return_lambdaComparer(x,y); } publicintGetHashCode(Tobj) { return_lambdaHash(obj); } }
很巧妙的采用了泛型委托的方式,实现只需要定义一个类实现IEqualityComparer
varnewList3=list.Distinct(newLambdaComparer((a,b)=>a.Id==b.Id&&a.Name==b.Name,obj=>obj.ToString().GetHashCode())).ToList();
是不是很熟悉的写法,想怎么比较就怎么比较,方便快捷,不需要定义那么多类去实现接口,目的达到。Linq中有很多扩展方法,都会用到IEqualityComparer
参考资料
1、https://www.nhooo.com/article/162602.htm
2、https://ask.helplib.com/c-Sharp/post_1277383
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。