前言
说到对集合去重处理,第一时间想到的肯定是Linq的Distinct扩展方式,对于一般的值类型集合去重,很好处理,直接list.Distinct()即可。但是如果想要对一个引用类型的集合去重(属性值都相同就认为重复),就会发现,直接Distinct()是不行的
先来看看泛型链表 List<T> 的定义:
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumera来源gaodai#ma#com搞@代~码$网ble<T>, IEnumerable
可见它实现了 IEnumerable<T>,而IEnumerable<T>规定了Distinct方法。
使用这个方法时要注意:
(1)该方法并不会改变原来的链表;
(2)该方法返回一个对象(假设叫做dis),通过该对象可以枚举原链表中的非重复元素,但是并没有把非重复元素复制一份到新的对象中(连签拷贝也没有)
(3)由于(2),在枚举dis时,始终是依赖于原有链表,所以如果在获得dis后,又更新了原有链表,那么使用dis枚举将会使用原有链表的最新状态。
var list=new List<SampleVersionDto>()///表明具有重复值得集合
有时候Distinct()不能对引用类型去重时 我们就要自定义了 自定义代码如下:
public class User { public int Id { get; set; } public string Name { get; set; } } var list = new List<User>() { new User() { Id = 1, Name = "张三" } , new User() { Id = 1, Name = "张三" } , new User() { Id = 3, Name = "李四" } , }; var newList1 = list.Distinct().ToList();
运行上述代码会发现,并不是预期想要的结果,newList1还是有3个元素。之所以会产生这样的结果,是因为Distinct()是通过使用默认的相等比较器对值进行比较返回序列中的非重复元素。对于值类型,默认的相等比较器是比较值是否相等,对于引用类型,默认的相等比较器是比较对象的引用地址,所以上述例子中即使属性值都相同,也不能去重。
IEqualityComparer<TSource>
聪明的我们,很容易就能发现,Linq已经为我们重载了一个去重方法,可以满足我们的需求:
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
重载的这个方法,多提供了一个参数IEqualityComparer<TSource> comparer,是一个泛型接口,我们只需要对这个接口进行实现,即可满足我们的去重需求:
public class UserComparer : IEqualityComparer<User> { public bool Equals(User x, User y) { return x.Id == y.Id && x.Name == y.Name; } public int GetHashCode(User obj) { return obj.ToString().GetHashCode(); } }
IEqualityComparer<TSource> 定义了两个方法,一个是Equals,一个是GetHashCode。这里我查找参考资料发现,进行比较时,默认先通过GetHashCode对两个元素进行比较,如果HashCode不同,则认为两个元素不同,如果相同则再通过Equals方法比较。所以这里我不能直接将User对象GetHashCode处理,而是先转换成了字符串再GetHashCode。通过这个重载方法,我们就可以到达目的了:
ar newList2 = list.Distinct(new UserComparer()).ToList();