61

I've read that Scala'a case class construct automatically generates a fitting equals and hashCode implementation. What does exactly the generated code look like?

  • 1
    possible duplicate of [What code is generated for an equals/hashCode method of a case class?](http://stackoverflow.com/questions/4526706/what-code-is-generated-for-an-equals-hashcode-method-of-a-case-class) – Suma Mar 13 '15 at 15:18

4 Answers4

91

As my professor used to say, only the code tells the truth! So just take a look at the code that is generated for:

case class A(i: Int, s: String)

We can instruct the Scala compiler to show us the generated code after the different phases, here after the typechecker:

% scalac -Xprint:typer test.scala
[[syntax trees at end of typer]]// Scala source: test.scala
package <empty> {
  @serializable case class A extends java.lang.Object with ScalaObject with Product {
    ..
    override def hashCode(): Int = ScalaRunTime.this._hashCode(A.this);
    ...
    override def equals(x$1: Any): Boolean = A.this.eq(x$1).||(x$1 match {
      case (i: Int,s: String)A((i$1 @ _), (s$1 @ _)) if i$1.==(i).&&(s$1.==(s)) => x$1.asInstanceOf[A].canEqual(A.this)
      case _ => false
    });


    override def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[A]()
  };
}

So you can see that the calculation of the hash code is delegated to ScalaRunTime._hashCode and the equality depends on the equality of the case class' members.

Mirko Stocker
  • 2,232
  • 16
  • 22
  • 6
    This not only explains it well, but taught me about `-Xprint:typer`. Thanks a lot! The only thing I'm confused about is what does `ScalaRunTime.this` mean? Why not simply `ScalaRunTime._hashCode`? –  May 03 '11 at 08:44
  • The `ClassName.this` syntax is usually used to access an outer `this` from within an inner class (that's the same as in Java). Not sure why it's printed here, maybe it's just the way the code is pretty printed by the compiler. But that's just a guess, anyone else? – Mirko Stocker May 03 '11 at 08:52
59

The generated hashCode just calls scala.runtime.ScalaRunTime._hashCode, which is defined as:

def _hashCode(x: Product): Int = {
  val arr =  x.productArity
  var code = arr
  var i = 0
  while (i < arr) {
    val elem = x.productElement(i)
    code = code * 41 + (if (elem == null) 0 else elem.hashCode())
    i += 1
  }
  code
}

So what you get is elem1 * 41**n + elem2 * 41**(n-1) .. elemn * 1, where n is the arity of your case class and elemi are the members of that case class.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Thanks for the clear answer. Now I don't know whether I should accept your answer or Mirko's answer, from which I also learnt the handy `-Xprint:typer` trick... –  May 03 '11 at 08:46
  • Together, both answers are perfectly answering the question :-) – Juh_ Oct 28 '16 at 08:02
19

Please be aware that the previous answers on this question are a bit outdated on the hashCode part.

As of scala 2.9 hashCode for case classes uses MurmurHash: link.

MurmurHash produces good avalanche effect, good distribution and is CPU friendly.

Community
  • 1
  • 1
Alexander Poluektov
  • 7,844
  • 1
  • 28
  • 32
1

Looks like things have changed; using Mirko's example case class A(i: Int, s: String)I get:

override <synthetic> def hashCode(): Int = {
      <synthetic> var acc: Int = -889275714;
      acc = scala.runtime.Statics.mix(acc, i);
      acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(s));
      scala.runtime.Statics.finalizeHash(acc, 2)
    };

and

override <synthetic> def equals(x$1: Any): Boolean = A.this.eq(x$1.asInstanceOf[Object]).||(x$1 match {
  case (_: A) => true
  case _ => false
}.&&({
      <synthetic> val A$1: A = x$1.asInstanceOf[A];
      A.this.i.==(A$1.i).&&(A.this.s.==(A$1.s)).&&(A$1.canEqual(A.this))
    }))
  };
Pete
  • 16,534
  • 9
  • 40
  • 54
  • It looks to me like `scala.runtime.ScalaRunTime._hashCode` still works, though, and based on the comments in the code, I'd expect it to produce the same result. ScalaRuntime delegates to MurmerHash3, which contains the comment: "Case objects have the hashCode inlined directly into the synthetic hashCode method, but this method should still give a correct result if passed a case object." – mbbush Apr 23 '18 at 18:44