如何使用自定义IEqualityComparer<T>结合LINQ实现double序列按精度去重
解决方案
可以直接复用LINQ的Distinct方法实现需求,但你当前的DistanceComparer存在关键缺陷,必须先修复GetHashCode的实现才能正常工作。
问题原因
LINQ的Distinct方法执行去重逻辑时,会先调用GetHashCode对元素做哈希分组,只有哈希值相同的元素才会进一步调用Equals判定是否相等。你当前的实现直接抛出异常,会导致运行时报错,且就算不抛异常,如果哈希值不满足「判定为相等的两个元素必须返回相同哈希值」的规则,Equals方法也根本不会被触发,去重逻辑完全失效。
修复后的比较器实现
public class DistanceComparer : IEqualityComparer<double> { public static IEqualityComparer<double> Default { get; } = new DistanceComparer(); // 容差阈值和缩放系数对应,容差为1e-6时按1e6缩放取整 private const double Tolerance = 1e-6; private const long Scale = 1000000; public bool Equals(double x, double y) { return Math.Abs(x - y) < Tolerance; } public int GetHashCode(double obj) { // 按容差缩放后取整,保证判定为相等的元素返回相同哈希码 long scaled = (long)Math.Round(obj * Scale); return scaled.GetHashCode(); } }
调用代码
直接调用Distinct的重载方法,传入自定义比较器即可:
var l = new List<double>() { 0, 0 + 1e-7, 1 - 1e-7, 1 }; var distinctResult = l.Distinct(DistanceComparer.Default).ToList(); // 最终输出结果为 [0, 1],符合预期
注意事项
- 这种基于容差的相等判断不严格满足相等关系的传递性:例如值A和B差0.8e-6、B和C差0.8e-6时,A和B、B和C会被判定为相等,但A和C差1.6e-6会被判定为不等,极端场景下可能出现不符合预期的去重结果。
- 如果对边界精度要求极高,也可以采用「先排序再相邻比较」的方式实现去重,避免哈希分组的边界误差:
var distinctResult = l.OrderBy(x => x) .Aggregate(new List<double>(), (list, current) => { if (!list.Any() || Math.Abs(current - list.Last()) >= 1e-6) list.Add(current); return list; });
内容的提问来源于stack exchange,提问作者malat
相关产品推荐
相关产品推荐

