c# list集合根据某个字段去重_利用Distinct()内置方法对List集合的去重问题详解

前言

说到对集合去重处理,第一时间想到的肯定是Linq的Distinct扩展方式,对于一般的值类型集合去重,很好处理,直接list.Distinct()即可。但是如果想要对一个引用类型的集合去重(属性值都相同就认为重复),就会发现,直接Distinct()是不行的

先来看看泛型链表 List 的定义:

public class List : IList, ICollection, IList, ICollection, IReadOnlyList, IReadOnlyCollection, IEnumerable, IEnumerable

可见它实现了 IEnumerable,而IEnumerable规定了Distinct方法。

使用这个方法时要注意:

(1)该方法并不会改变原来的链表;

(2)该方法返回一个对象(假设叫做dis),通过该对象可以枚举原链表中的非重复元素,但是并没有把非重复元素复制一份到新的对象中(连签拷贝也没有)

(3)由于(2),在枚举dis时,始终是依赖于原有链表,所以如果在获得dis后,又更新了原有链表,那么使用dis枚举将会使用原有链表的最新状态。

var list=new List()///表明具有重复值得集合

有时候Distinct()不能对引用类型去重时 我们就要自定义了 自定义代码如下:

public class User

{

public int Id { get; set; }

public string Name { get; set; }

}

var list = new List()

{

new User() { Id = 1, Name = "张三" } ,

new User() { Id = 1, Name = "张三" } ,

new User() { Id = 3, Name = "李四" } ,

};

var newList1 = list.Distinct().ToList();

运行上述代码会发现,并不是预期想要的结果,newList1还是有3个元素。之所以会产生这样的结果,是因为Distinct()是通过使用默认的相等比较器对值进行比较返回序列中的非重复元素。对于值类型,默认的相等比较器是比较值是否相等,对于引用类型,默认的相等比较器是比较对象的引用地址,所以上述例子中即使属性值都相同,也不能去重。

IEqualityComparer

聪明的我们,很容易就能发现,Linq已经为我们重载了一个去重方法,可以满足我们的需求:

public static IEnumerable Distinct(this IEnumerable source, IEqualityComparer comparer);

重载的这个方法,多提供了一个参数IEqualityComparer comparer,是一个泛型接口,我们只需要对这个接口进行实现,即可满足我们的去重需求:

public class UserComparer : IEqualityComparer

{

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 定义了两个方法,一个是Equals,一个是GetHashCode。这里我查找参考资料发现,进行比较时,默认先通过GetHashCode对两个元素进行比较,如果HashCode不同,则认为两个元素不同,如果相同则再通过Equals方法比较。所以这里我不能直接将User对象GetHashCode处理,而是先转换成了字符串再GetHashCode。通过这个重载方法,我们就可以到达目的了:

ar newList2 = list.Distinct(new UserComparer()).ToList();

甚至我们还可以实现只要某个属性相同就认为重复的效果,只需要在Equals方法按想要比较方式进行处理即可

延伸思考

Distinct的重载方法,基本已经能够满足我们的各式各样的去重需求了,但是想来想去,还是觉得有点别扭,那就是如果有类似的去重需求,我们都要新增一个类去实现IEqualityComparer接口,不够灵活,本着封装重用的原则,想了想能否在这方面进行优化。恰巧最近在搞一个Android项目,学习了一下java,了解到java有一个匿名实现接口的语法特性,如果C#也能匿名实现接口,那就不需要增加那么多类去实现接口,会方便很多。很遗憾C#中没有这个特性,看了下资料我感觉java其实也不算是真正意义上的匿名实现,它是编译器做了手脚,编译的时候生成了一个真实的类去实现接口。在一番查找资料后,终于找到了一个很好的解决方案:

public class LambdaComparer : IEqualityComparer

{

private readonly Func _lambdaComparer;

private readonly Func _lambdaHash;

public LambdaComparer(Func lambdaComparer)

: this(lambdaComparer, EqualityComparer.Default.GetHashCode)

{

}

public LambdaComparer(Func lambdaComparer, Func lambdaHash)

{

if (lambdaComparer == null)

throw new ArgumentNullException("lambdaComparer");

if (lambdaHash == null)

throw new ArgumentNullException("lambdaHash");

_lambdaComparer = lambdaComparer;

_lambdaHash = lambdaHash;

}

public bool Equals(T x, T y)

{

return _lambdaComparer(x, y);

}

public int GetHashCode(T obj)

{

return _lambdaHash(obj);

}

}

很巧妙的采用了泛型委托的方式,实现只需要定义一个类实现IEqualityComparer接口,Equals、GetHashCode的实现,由传入的委托方法决定,接下来就简单了

var newList3 = list.Distinct(new LambdaComparer((a, b) => a.Id == b.Id && a.Name == b.Name, obj => obj.ToString().GetHashCode())).ToList();

是不是很熟悉的写法,想怎么比较就怎么比较,方便快捷,不需要定义那么多类去实现接口,目的达到。Linq中有很多扩展方法,都会用到IEqualityComparer接口。通过这种方式,可以大大提高重用率

参考资料

1、https://www.jb51.net/article/162602.htm

2、https://ask.helplib.com/c-Sharp/post_1277383

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

本文标题: 利用Distinct()内置方法对List集合的去重问题详解

本文地址: http://www.cppcns.com/ruanjian/csharp/261755.html

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值