自定义 IEqualityComparer 比使用 .Where() + .Any() 更快地从两个列表中获取不同的对象

Custom IEqualityComparer to get distinct objects from two lists faster than using .Where() + .Any()

所以我有两个对象列表,对象有多个字段,但我想仅根据其中两个来区分它们。

给你图片,对象KeyAndValue由字段Key和Tag组成,所以:

list1 = { obj1(key=1,tag=A), obj2(key=2,tag=A) }
list2 = { obj3(key=1,tag=A), obj4(key=2,tag=B) }

我目前正在使用:

list1.Where(x => !list2.Any(y => y.key == x.key)).ToList();

正确的结果是:obj1、obj2 和 obj4,因为 obj3 与 obj1 具有相同的键和标签

我想要完成的是加快这个过程,因为很多对象需要很长时间。我发现自定义 IEqualityComparer 在这里可以提供帮助,所以我基于 MS Specification 编写了自己的 它看起来像这样:

class KeyComparer : IEqualityComparer<KeyAndValue>
{
    public bool Equals(KeyAndValue x, KeyAndValue y)
    {
        if (Object.ReferenceEquals(x, y))
            return true;
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;
        return x.key == y.key && x.tag == y.tag;
    }
    public int GetHashCode(KeyAndValue keyAndValue)
    {
        if (Object.ReferenceEquals(keyAndValue, null))
            return 0;
        int hashKeyAndValueKey = keyAndValue.key == null ? 0 : keyAndValue.key.GetHashCode();
        int hashKeyAndValueTag = keyAndValue.tag == null ? 0 : keyAndValue.tag.GetHashCode();
        return hashKeyAndValueKey ^ hashKeyAndValueTag;
    }
}

我是这样使用的:

list1.Except(list2, new KeyComparer()).ToList();

不幸的是,它只会从 list2 中删除重复项。似乎它甚至没有触及 list1,我不知道这是否是我的自定义比较器的错误,我使用它的方式或者我应该使用另一种方法。我一直在查看其他问题,但找不到有效的答案(或者至少是我实际上知道如何正确实施的答案)。

我认为你不需要Except。您希望两者具有不同的值?

var distinctValues = list1.Union(list2).Distinct();

您需要在 KeyAndValue 中实现 GetHashCode/Equals 或使用比较器按键和值比较对象。


(下面是老东西)

不确定我是否理解正确。难道是你没认出来Except new IEnumerable and ToList a new List?

尝试:

var list1AdditionalItems = list1.Except(list2, new KeyComparer()).ToList();

可能还有:

var list2AdditionalItems = list2.Except(list1, new KeyComparer()).ToList();

另一个观察。在此代码中:

list1.Where(x => !list2.Any(y => y.key == x.key)).ToList();

你只检查密钥。如果你想要这种行为,你应该相应地编写比较器:

class KeyComparer : IEqualityComparer<KeyAndValue>
{
    public bool Equals(KeyAndValue x, KeyAndValue y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;
        return x.key == y.key;
    }
    public int GetHashCode(KeyAndValue keyAndValue)
    {
        if (ReferenceEquals(keyAndValue, null))
            return 0;
        return keyAndValue.key == null ? 0 : keyAndValue.key.GetHashCode();
    }
}

最后但同样重要的是:当您需要性能时,请考虑改用字典。

正如 Stefan Steinegger(我非常感谢他付出的努力和时间)在第一条评论中提到的,我的两种方法都没有 return obj4 我发现了问题并决定实施完全不同的方法。现在我的KeyAndValueclass也有一个int Hash字段,当调用构造函数时Hash字段被填充key.GetHashCode() ^ tag.GetHashCode()。它简化了比较,因为现在我首先将 list1 与 list2 组合,然后通过发送它:CombinedList.GroupBy(x => x.Hash).Select(y => y.First()).ToList(); 结果似乎是正确的:)