Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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, rather than default object equality test

I have tried overriding the == operator, but it isn't being used.

Thanks in advance.

Edit:

The intersect method is a concrete value member of GenSetLike

spec: http://www.scala-lang.org/api/current/scala/collection/GenSetLike.html src: 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 

so the intersection is done using the filter method.

Yet another Edit:

filter is defined in TraversableLike

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

src: 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 } 

What's unclear to me is what it uses when invoked without a predicate, p. That's not an implicit parameter.

like image 365
Kareem Avatar asked Oct 06 '11 22:10

Kareem


1 Answers

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)

like image 197
Didier Dupont Avatar answered Oct 24 '22 14:10

Didier Dupont