You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中HashSet<List<float>>调用SetEquals返回false但预期为true

这个问题的核心原因其实很简单——HashSet<T>默认用的是引用相等来判断List<float>这类引用类型的元素是否相同,而不是比较它们的内容!

你用HashSet<int>没问题,是因为int是值类型,默认的相等比较就是比较值本身;但List<float>是引用类型,哪怕两个List的内容完全一样,只要它们是不同的对象实例,EqualityComparer<List<float>>.Default就会认为它们不相等,这就导致SetEquals返回false了。

接下来给你两种实用的解决方案:

方案一:自定义相等比较器(推荐,改动最小)

我们可以写一个实现IEqualityComparer<List<float>>的类,让HashSet用这个比较器来判断List的内容是否相等,而不是看引用。

先写比较器类:

public class ListFloatEqualityComparer : IEqualityComparer<List<float>>
{
    public bool Equals(List<float> x, List<float> y)
    {
        // 处理null的边界情况
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;
        // 长度不同直接不相等
        if (x.Count != y.Count) return false;
        
        // 逐个比较元素,这里要注意float的精度问题:
        // 如果你的数值是精确的(比如字面量赋值),直接用==就行;
        // 如果是计算得到的,建议用阈值判断(比如Math.Abs(x[i]-y[i]) < 1e-6)
        for (int i = 0; i < x.Count; i++)
        {
            if (x[i] != y[i]) return false;
        }
        return true;
    }

    public int GetHashCode(List<float> obj)
    {
        if (obj == null) return 0;
        int hash = 17;
        // 结合每个元素的哈希码生成List的哈希值
        foreach (float f in obj)
        {
            hash = hash * 31 + f.GetHashCode();
        }
        return hash;
    }
}

然后在创建HashSet的时候,把这个比较器传进去:

// 初始化HashSet时指定自定义比较器
var thisList = new HashSet<List<float>>(new ListFloatEqualityComparer());
var otherList = new HashSet<List<float>>(new ListFloatEqualityComparer());

这样之后,thisList.SetEquals(otherList)就会正确识别内容相同的List为相等元素,返回true了。

注意:SetEquals没有重载可以单独指定比较器,所以必须在HashSet创建时就传入比较器——因为HashSet内部的哈希表是基于创建时的比较器构建的。

方案二:改用自定义值类型(更优雅,适合长期使用)

如果你不想每次创建HashSet都传比较器,可以把List<float>换成一个自定义的不可变值类型,重写Equals和GetHashCode来实现内容比较:

public readonly struct FloatList : IEquatable<FloatList>
{
    private readonly List<float> _items;

    public FloatList(IEnumerable<float> items)
    {
        _items = new List<float>(items);
    }

    public bool Equals(FloatList other)
    {
        if (_items.Count != other._items.Count) return false;
        for (int i = 0; i < _items.Count; i++)
        {
            if (_items[i] != other._items[i]) return false;
        }
        return true;
    }

    public override bool Equals(object obj)
    {
        return obj is FloatList other && Equals(other);
    }

    public override int GetHashCode()
    {
        int hash = 17;
        foreach (float f in _items)
        {
            hash = hash * 31 + f.GetHashCode();
        }
        return hash;
    }

    public static bool operator ==(FloatList left, FloatList right)
    {
        return left.Equals(right);
    }

    public static bool operator !=(FloatList left, FloatList right)
    {
        return !left.Equals(right);
    }
}

之后用HashSet<FloatList>来存储数据,默认的相等比较器就会按内容判断相等了,不需要额外传比较器:

var thisList = new HashSet<FloatList>();
thisList.Add(new FloatList(new List<float> {10.0f, 15.0f}));
// 同理初始化otherList...

补充:关于float精度的小提示

如果你的float数值是通过计算得到的(不是直接字面量赋值),直接用==比较可能会因为精度误差导致误判,这时候可以把Equals方法里的比较改成:

if (Math.Abs(x[i] - y[i]) > 1e-6) // 阈值可以根据你的业务需求调整
    return false;

这样就能避免微小的精度差异导致的错误判断啦。

内容的提问来源于stack exchange,提问作者Alex Tereshenkov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:00:36