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

时间:2017-04-05 07:41:31

标签: c# list object distinct iequalitycomparer

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

为了给你提供图片,对象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 as 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,我不知道这是我的自定义比较器的错,我使用它的方式或者我应该使用另一种方法。我一直在查看其他问题,但找不到合适的答案(或者至少有一个我真正知道如何正确实施的答案)。

2 个答案:

答案 0 :(得分:0)

我认为你不需要Except。你想拥有两者的不同价值吗?

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

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

(老东西吼叫)

不确定我是否正确理解了这个问题。可能是你没有认识到除了创建一个新的IEnumerable和ToList一个新的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();
    }
}

最后但并非最不重要:当您需要表现时,请考虑使用字典。

答案 1 :(得分:0)

正如Stefan Steinegger(我感谢他们花费的精力和时间)在第一篇评论中提到的,我的方法都没有返回obj4,我发现了一个问题,并决定采用完全不同的方法。现在我的KeyAndValue类还有一个int Hash字段,当一个构造函数被调用时,Hash字段填充了key.GetHashCode() ^ tag.GetHashCode()。它简化了比较,因为我现在首先将list1与list2合并,然后通过它发送:CombinedList.GroupBy(x => x.Hash).Select(y => y.First()).ToList();结果似乎是正确的:)