How can I define a custom equality operation that will be used by immutable Set comparison methods

后端 未结 4 641
南笙
南笙 2020-12-05 09:46

I have an immutable Set of a class, Set[MyClass], and I want to use the Set methods intersect and diff, but I want them to test for equality using my custom equals method, r

相关标签:
4条回答
  • 2020-12-05 09:47

    You'll need to override .hashCode as well. This is almost always the case when you override .equals, as .hashCode is often used as a cheaper pre-check for .equals; any two objects which are equal must have identical hash codes. I'm guessing you're using objects whose default hashCode does not respect this property with respect to your custom equality, and the Set implementation is making assumptions based on the hash codes (and so never even calling your equality operation).

    See the Scala docs for Any.equals and Any.hashCode: http://www.scala-lang.org/api/rc/scala/Any.html

    0 讨论(0)
  • 2020-12-05 09:56

    equals and hashCode are provided automatically in case class only if you do not define them.

    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))
    

    If what you want is reference equality, still write equals and hashCode, to prevent automatic generation, and call the version from AnyRef

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

    With that:

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

    You cannot override the ==(o: Any) from AnyRef, which is sealed and always calls equals. If you tried defining a new (overloaded) ==(m: MyClass), it is not the one that Set calls, so it is useless here and quite dangerous in general.

    As for the call to filter, the reason it works is that Set[A] is a Function[A, Boolean]. And yes, equals is used, you will see that function implementation (apply) is a synonymous for contains, and most implementations of Set use == in contains (SortedSet uses the Ordering instead). And == calls equals.


    Note: the implementation of my first equals is quick and dirty and probably bad if MyClass is to be subclassed . If so, you should at the very least check type equality (this.getClass == that.getClass) or better define a canEqual method (you may read this blog by Daniel Sobral)

    0 讨论(0)
  • 2020-12-05 09:57

    This answer shows a custom mutable Set with user-defined Equality. It could be made immutable by replacing the internal store with a Vector and returning a modified copy of itself upon each operation

    0 讨论(0)
  • 2020-12-05 10:09

    "It is not possible to override == directly, as it is defined as a final method in class Any. That is, Scala treats == as if were defined as follows in class Any:

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

    " from Programming In Scala, Second Edition

    0 讨论(0)
提交回复
热议问题