【问题标题】:Intersect with a custom IEqualityComparer using Linq使用 Linq 与自定义 IEqualityComparer 相交
【发布时间】:2010-12-02 21:44:25
【问题描述】:

长话短说:我有 2 个对象集合。一个包含好的值(我们称它为“Good”),另一个包含默认值(Mr.“Default”)。我想要 Good 和 Default 以及 Default 之间的联合交集。换句话说:相交(联合(良好,默认),默认)。有人可能会认为它解析为默认值,但这里是棘手的地方:我使用自定义 IEqualityComparer。

我有以下课程:

class MyClass
{
    public string MyString1;
    public string MyString2;
    public string MyString3;
}

class MyEqualityComparer : IEqualityComparer<MyClass>
{
    public bool Equals(MyClass item1, MyClass item2)
    {
        if(item1 == null && item2 == null)
            return true;
        else if((item1 != null && item2 == null) ||
                (item1 == null && item2 != null))
            return false;

        return item1.MyString1.Equals(item2.MyString1) &&
               item1.MyString2.Equals(item2.MyString2);
    }

    public int GetHashCode(MyClass item)
    {
        return new { item.MyString1, item.MyString2 }.GetHashCode();
    }
}

以下是我的收藏品的特征 Good 和 Default 收藏品:

默认值:这是一个很大的集合,包含所有想要的 { MyString1, MyString2 } 对,但 MyString3 值是您可以猜到的默认值。

Good :这是一个较小的集合,主要包含默认集合中的项目,但具有一些好的 MyString3 值。它还有一些 { MyString1, MyString2 } 超出了想要的集合。

我想要做的是:只从 Good 中取出 Default 中的项目,但将 Default 中的其他项目添加到该项目中。

这是,我认为是,我最好的尝试:

HalfWantedResult = Good.Union(Default, new MyEqualityComparer());
WantedResult= HalfWantedResult.Intersect(Good, new MyEqualityComparer());

我教它应该可以工作,但我得到的结果基本上只有好的 { MyString1, MyString2 } 对集,但都来自默认集,所以我有默认值。我也尝试切换最后一个相交的默认和良好,但我得到了相同的结果。

【问题讨论】:

  • 你的 Equals 实现真的很糟糕。会有不应该存在的哈希冲突。为什么不使用相同的投影 (new { item.MyString1, item.MyString2 }) 而是调用 Equals?
  • 我应该调查一下,这可能是问题的一部分。 Union 使用 GetHashCode,Intersects 使用 Equals。我还没有真正投入任何教导。 惭愧

标签: c# linq intersection intersect


【解决方案1】:

首先这是错误的:

public bool Equals(MyClass item1, MyClass item2)
{
    return GetHashCode(item1) == GetHashCode(item2);
}

如果hashcode不同肯定对应的2项不同,但如果相等则不保证对应的2项相等。

所以这是正确的Equals 实现:

public bool Equals(MyClass item1, MyClass item2)
{
    if(object.ReferenceEquals(item1, item2))
        return true;
    if(item1 == null || item2 == null)
        return false;
    return item1.MyString1.Equals(item2.MyString1) &&
           item1.MyString2.Equals(item2.MyString2);
}

作为Slacks suggested(期待我),代码如下:

var Default = new List<MyClass>
{
    new MyClass{MyString1="A",MyString2="A",MyString3="-"},
    new MyClass{MyString1="B",MyString2="B",MyString3="-"},
    new MyClass{MyString1="X",MyString2="X",MyString3="-"},
    new MyClass{MyString1="Y",MyString2="Y",MyString3="-"},
    new MyClass{MyString1="Z",MyString2="Z",MyString3="-"},

};
var Good = new List<MyClass>
{
    new MyClass{MyString1="A",MyString2="A",MyString3="+"},
    new MyClass{MyString1="B",MyString2="B",MyString3="+"},
    new MyClass{MyString1="C",MyString2="C",MyString3="+"},
    new MyClass{MyString1="D",MyString2="D",MyString3="+"},
    new MyClass{MyString1="E",MyString2="E",MyString3="+"},
};
var wantedResult = Good.Intersect(Default, new MyEqualityComparer())
                       .Union(Default, new MyEqualityComparer());

// wantedResult:
// A A +
// B B +
// X X -
// Y Y -
// Z Z -

【讨论】:

  • +1 在我的 Equals 方面帮助了我很多,但没有被接受,因为它不能解决问题。 (我希望我能 +2!:-P)
  • 没有问题。我正在为您的交叉路口问题编写正确的解决方案,但 Slacks 预料到了我。他就是答案;-)
  • 我觉得自己好蠢...我误解了有关 intersect 方法的工具提示,并且我的基础数据中有错误,因此无法发生匹配。由于我认为这篇文章对 equals 的实现和示例最有帮助,因此我接受了它。再次感谢。
  • 不客气 ;-)。顺便说一句,如果您的 Comparer 被调用的次数非常多,并且性能对您来说真的很重要,您应该考虑重写您的 GetHashCode() 以避免为每次调用创建一个匿名类型的新实例。查看这个答案以获得快速有效的实施 --> stackoverflow.com/questions/263400/…
  • Equals 的实现并不理想。序言很难读。请考虑将if (object.ReferenceEquals(item1,item2)) return true; 作为第一行。这处理了两者都为空的情况,加快了两者都是相同参考的情况。那么剩下的空检查就是if(item1==null||item2==null) return false;,它更短更容易阅读,而且是正确的,因为如果它们都是空的,你已经返回了true。
【解决方案2】:

您需要检查实际相等,而不仅仅是哈希码相等。

GetHashCode() 不是(也不能)无冲突,这就是为什么首先需要Equals 方法。

此外,您可以通过编写更简单地做到这一点

WantedResult = Good.Concat(Default).Distinct();

Distinct 方法将返回每对重复项的第一项,因此这将返回所需的结果。

编辑:应该是

WantedResult = Good.Intersect(Default, new MyEqualityComparer())
                   .Union(Default, new MyEqualityComparer());

【讨论】:

  • 如果我使用 Concat & Distinct,我会得到我想要的结果,但我也会有一些来自 Good 集合的项目,这些项目不在想要的范围内(默认)。跨度>
  • Union 和 Intersect 操作是可交换的,所以它不会改变结果,但是是的,它避免了使用中间人变量。
  • 哦,等等,Union 和 Intersect 不可交换:(ABC u ABD) n AE = A(ABC n AE) u ABD = ABD
  • 哦,对不起,我只考虑我的情况:(ABC u ABD) n ABC = ABC 和 ABC u (ABD n ABC) = ABC。但是,是的,你是对的,他们不是。就我而言,(ABC u ABD) != (ABD u ABC),因为它不是真正的联合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-20
  • 2014-12-11
  • 2010-09-30
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多