【问题标题】:How can I define a custom equality operation that will be used by immutable Set comparison methods如何定义不可变的 Set 比较方法将使用的自定义相等操作
【发布时间】:2011-12-02 15:07:21
【问题描述】:

我有一个类的不可变 Set,Set[MyClass],我想使用 Set 方法 intersect 和 diff,但我希望他们使用我的自定义 equals 方法而不是默认对象相等性测试来测试相等性

我已经尝试覆盖 == 运算符,但它没有被使用。

提前致谢。

编辑:

intersect方法是GenSetLike的具体值成员

规格:http://www.scala-lang.org/api/current/scala/collection/GenSetLike.html 源:https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/GenSetLike.scala#L1

def intersect(that: GenSet[A]): Repr = this filter that

所以交集是使用过滤方法完成的。

又一个编辑:

过滤器在 TraversableLike 中定义

规格:http://www.scala-lang.org/api/current/scala/collection/TraversableLike.html

源代码:https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/TraversableLike.scala#L1

def filter(p: A => Boolean): Repr = {
  val b = newBuilder
      for (x <- this) 
        if (p(x)) b += x
      b.result
}

我不清楚的是它在没有谓词的情况下调用时使用什么,p。这不是隐式参数。

【问题讨论】:

  • 您是否尝试过覆盖 .equals?

标签: scala set overriding


【解决方案1】:

equals 和 hashCode 仅在您未定义它们时才会在 case 类中自动提供。

case class MyClass(val name: String) {
  override def equals(o: Any) = o match {
    case that: MyClass => that.name.equalsIgnoreCase(this.name)
    case _ => false
  }
  override def hashCode = name.toUpperCase.hashCode
}

Set(MyClass("xx"), MyClass("XY"), MyClass("xX"))
res1: scala.collection.immutable.Set[MyClass] = Set(MyClass(xx), MyClass(XY))

如果要引用相等,还​​是写equals和hashCode,防止自动生成,从AnyRef调用版本

  override def equals(o: Any) = super.equals(o)
  override def hashCode = super.hashCode

这样:

Set(MyClass("x"), MyClass("x"))
res2: scala.collection.immutable.Set[MyClass] = Set(MyClass(x), MyClass(x))

您不能从 AnyRef 覆盖 ==(o: Any),它是密封的并且总是调用 equals。如果你尝试定义一个新的(重载的)==(m: MyClass),它不是Set 调用的那个,所以它在这里是没用的,而且一般来说非常危险。

至于对filter 的调用,它起作用的原因是Set[A]Function[A, Boolean]。是的,使用了equals,你会看到函数实现(apply)是contains的同义词,而Set的大多数实现在包含中使用==SortedSet使用Ordering反而)。并且== 调用equals


注意:我的第一个 equals 的实现是快速而肮脏的,如果要对 MyClass 进行子类化可能会很糟糕。如果是这样,您至少应该检查类型相等性 (this.getClass == that.getClass) 或更好地定义 canEqual 方法(您可以阅读 Daniel Sobral 的 this blog

【讨论】:

  • 这成功了,谢谢!两个答案都是正确的,但我选择了这个,因为它提供了完整的解释。
  • 不,不是我的博客! Programming in Scala 上的第 28 章死得更好。
  • 对不起,我会尝试不再这样做;-)
  • 小记:这是Scala编程第二版的第30章
  • 如果我的课不是案例课怎么办?
【解决方案2】:

您还需要覆盖.hashCode。当您覆盖.equals 时,几乎总是会出现这种情况,因为.hashCode 通常用作.equals 的更便宜的预检查;任何两个相等的对象必须具有相同的哈希码。我猜您正在使用默认 hashCode 不尊重此属性的对象的自定义相等性,并且 Set 实现基于哈希码做出假设(因此甚至从未调用您的相等性操作)。

请参阅 Any.equalsAny.hashCode 的 Scala 文档:http://www.scala-lang.org/api/rc/scala/Any.html

【讨论】:

  • 对不起,我遗漏了一个潜在的关键点。这是案例类。我实施了您建议的更改,但没有任何效果。对于案例类:equals 方法会自动重新定义,以在结构上而不是通过身份比较同一案例类的两个实例。 hashCode 方法被自动重新定义为使用构造函数参数的 hashCodes。
  • 此外,.equals 是否甚至用于这些方法中的比较?从 src 中可以看出,在 Set 上使用了过滤器,带有一些未知的谓词。
【解决方案3】:

This answer shows a custom mutable Set with user-defined Equality。可以通过将内部存储替换为Vector 并在每次操作时返回其自身的修改副本来使其不可变

【讨论】:

    【解决方案4】:

    “不能直接覆盖==,因为它被定义为Any类中的最终方法。也就是说,Scala将==视为在Any类中定义如下:

        final def == (that: Any): Boolean =
          if (null eq this) {null eq that} else {this equals that}
    

    " 来自 Programming In Scala, Second Edition

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-11
      • 2012-11-11
      • 2017-06-07
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多