说到对集合去重处理,第一时间想到的肯定是Linq的不同扩展方式,对于一般的值类型集合去重,很好处理,直接list.Distinct()即可。但是如果想要对一个引用类型的集合去重(属性值都相同就认为重复),就会发现,直接不同的()是不行的
先来看看泛型链表List
公共类List
引用>:IList ICollection IReadOnlyCollection IEnumerable IEnumerable 可见它实现了IEnumerable
,而IEnumerable 规定了不同的方法。
(1)该方法并不会改变原来的链表;
(2)该方法返回一个对象(假设叫做说),通过该对象可以枚举原链表中的非重复元素,但是并没有把非重复元素复制一份到新的对象中(连签拷贝也没有)
(3)由于(2),在枚举说时,始终是依赖于原有链表,所以如果在获得说后,又更新了原有链表,那么使用说枚举将会使用原有链表的最新状态。
var=new List<列表;SampleVersionDto>()///表明具有重复值得集合有时候截然不同()不能对引用类型去重时我们就要自定义了自定义代码如下:
公开课用户 { 公共int Id{得到;设置;} 公共字符串名称{;设置;} } var=new List<列表;User> () { 新用户(){Id=1, Name="张三"}, 新用户(){Id=1, Name="张三"}, 新用户(){Id=3, Name="李四"}, }; var newList1=list.Distinct () .ToList ();运行上述代码会发现,并不是预期想要的结果,newList1还是有3个元素。之所以会产生这样的结果,是因为不同的()是通过使用默认的相等比较器对值进行比较返回序列中的非重复元素。对于值类型,默认的相等比较器是比较值是否相等,对于引用类型,默认的相等比较器是比较对象的引用地址,所以上述例子中即使属性值都相同,也不能去重。
聪明的我们,很容易就能发现,Linq已经为我们重载了一个去重方法,可以满足我们的需求:
公共静态IEnumerableDistinct (这IEnumerable 源,IEqualityComparer 比较器); 重载的这个方法,多提供了一个参数IEqualityComparer
比较器,是一个泛型接口,我们只需要对这个接口进行实现,即可满足我们的去重需求: 公共类UserComparer: IEqualityComparer{ 公共bool=(用户x, y) { 返回x。Id==y。Id,,x。==y.Name名称; } 公共int GetHashCode方法(用户obj) { .GetHashCode返回obj.ToString () (); } } IEqualityComparer
定义了两个方法,一个是等于,一个是GetHashCode方法。这里我查找参考资料发现,进行比较时,默认先通过GetHashCode方法对两个元素进行比较,如果HashCode不同,则认为两个元素不同,如果相同则再通过=方法比较,所以这里我不能直接将用户对象GetHashCode方法处理,而是先转换成了字符串再GetHashCode方法。通过这个重载方法,我们就可以到达目的了: 基于“增大化现实”技术newList2=列表。不同的(新UserComparer ()) .ToList ();甚至我们还可以实现只要某个属性相同就认为重复的效果,只需要在=方法按想要比较方式进行处理即可
不同的重载方法,基本已经能够满足我们的各式各样的去重需求了,但是想来想去,还是觉得有点别扭,那就是如果有类似的去重需求,我们都要新增一个类去实现IEqualityComparer
接口,不够灵活,本着封装重用的原则,想了想能否在这方面进行优化。恰巧最近在搞一个Android项目,学习了一java,下了解到java有一个匿名实现接口的语法特性,如果c#也能匿名实现接口,那就不需要增加那么多类去实现接口,会方便很多。很遗憾c#中没有这个特性,看了下资料我感觉java其实也不算是真正意义上的匿名实现,它是编译器做了手脚,编译的时候生成了一个真实的类去实现接口。在一番查找资料后,终于找到了一个很好的解决方案: 公开课LambdaComparer:IEqualityComparer { 私人只读的Func _lambdaComparer; 私人只读的Func _lambdaHash; 公共LambdaComparer (Func lambdaComparer) :这(lambdaComparer EqualityComparer 利用不同的()内置方法对集列表合的去重问题详解