case classで自動的に実装されるhashCodeについて


case classを使うと`equals`や`hashCode`が自動的に実装されます。
参考：[ひしだまさんのScalaクラスメモ](http://www.ne.jp/asahi/hishidama/home/tech/scala/class.html#h_case_class)
`hashCode`の実装は具体的にどんなコードが生成されているか、が気になって調べました。

**注意：概要程度分かれば十分だったので、そこまで詳細に踏み入ってないです。**

# 調べ方

[Stack Overflowの回答](http://stackoverflow.com/questions/5866720/hashcode-in-case-classes-in-scala)のように`-Xprint:typer`を付けることでREPLでも簡単に確認することができます。
なお、SBTコンソールで設定したい場合は`set scalacOptions in (Compile, console) := "-Xprint:typer"`を指定できます。

# サンプル

Scala 2.11.8

## プリミティブでない場合

```scala
case class A(x: Seq[String])
```

だと、こんな感じになります。

```
case class A extends AnyRef with Product with Serializable {
  <caseaccessor> <paramaccessor> private[this] val x: Seq[String] = _;
  <stable> <caseaccessor> <accessor> <paramaccessor> def x: Seq[String] = A.this.x;
  def <init>(x: Seq[String]): A = {
    A.super.<init>();
    ()
  };
  <synthetic> def copy(x: Seq[String] = x): A = new A(x);
  <synthetic> def copy$default$1: Seq[String] = A.this.x;
  override <synthetic> def productPrefix: String = "A";
  <synthetic> def productArity: Int = 1;
  <synthetic> def productElement(x$1: Int): Any = x$1 match {
    case 0 => A.this.x
    case _ => throw new IndexOutOfBoundsException(x$1.toString())
  };
  override <synthetic> def productIterator: Iterator[Any] = runtime.this.ScalaRunTime.typedProductIterator[Any](A.this);
  <synthetic> def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[A]();
  override <synthetic> def hashCode(): Int = ScalaRunTime.this._hashCode(A.this);
  override <synthetic> def toString(): String = ScalaRunTime.this._toString(A.this);
  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.x.==(A$1.x).&&(A$1.canEqual(A.this))
    }))
};
<synthetic> object A extends scala.runtime.AbstractFunction1[Seq[String],A] with Serializable {
  def <init>(): A.type = {
    A.super.<init>();
    ()
  };
  final override <synthetic> def toString(): String = "A";
  case <synthetic> def apply(x: Seq[String]): A = new A(x);
  case <synthetic> def unapply(x$0: A): Option[Seq[String]] = if (x$0.==(null))
    scala.this.None
  else
    Some.apply[Seq[String]](x$0.x);
  <synthetic> private def readResolve(): Object = $iw.this.A
}
```

hashCodeの部分は

```
override <synthetic> def hashCode(): Int = ScalaRunTime.this._hashCode(A.this);
```

となってます。このScalaRuntimeは`scala.runtime`パッケージに含まれます。
どんな実装になっているかというと`def _hashCode(x: Product): Int = scala.util.hashing.MurmurHash3.productHash(x)`と更にhashingパッケージのメソッドを呼び出します。
その中身だけ抜粋すると、こうなっています。

```scala
final val productSeed = 0xcafebabe
def productHash(x: Product): Int = productHash(x, productSeed)

/** Compute the hash of a product */
final def productHash(x: Product, seed: Int): Int = {
  val arr = x.productArity
  // 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.
  if (arr == 0) {
    x.productPrefix.hashCode
  }
  else {
    var h = seed
    var i = 0
    while (i < arr) {
      h = mix(h, x.productElement(i).##)
      i += 1
    }
    finalizeHash(h, arr)
  }
}
```

[productElement](http://www.scala-lang.org/files/archive/api/current/#scala.Product)で逐次`##`を呼んでいます。（後述しますが`##`だとnullが来ても落ちない。）
ちなみに、Listの`hashCode`はどう実装されているかというと、`LinearSeqLike`のなかで

```scala
override def hashCode()= scala.util.hashing.MurmurHash3.seqHash(seq) // TODO - can we get faster via "linearSeqHash" ?
```

という記述が見つかります。（コレクションは継承関係が複雑なので見誤っている可能性アリ）
この`seqHash`の中は、まぁ、予想通りで1要素ずつIterateしながらハッシュ値を計算します。
`linearSeqHash`とは一体何者なのか……

なので、例えばCompositeな構造のオブジェクトのハッシュ値を取得しようとすると、再帰的にハッシュ値を求めていくことになります。
末尾再帰最適化がされたりするわけでないと思うので、ちょっと注意が必要かなぁという感じですね。

## プリミティブな場合

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

な感じだとこうなります。

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

同じProductですが、フィールドの種類に応じて生成されるコードが変わるという点もポイントですね。

# 余談

## `hashCode`と`##`

どちらもハッシュ値を算出しますが、厳密には挙動が異なります。
`##`の[APIリファレンス](http://www.scala-lang.org/files/archive/api/current/index.html#scala.Any)を見ると、次のように記載があります。
（ボクシングされた）数値型とnull以外は`hashCode`と同じだけれど、数値型はその値自身と同じ値をハッシュ値として、nullは0になります。

> Equivalent to x.hashCode except for boxed numeric types and null. For numerics, it returns a hash value which is consistent with value equality: if two value type instances compare as true, then ## will produce the same hash value for each of them. For null returns a hashcode where null.hashCode throws a NullPointerException.

ちなみにですが、Stack Overflowのこの[回答](http://stackoverflow.com/a/21436487)ではLongの`hashCode`と`##`は異なりますが、2.11.8では同じ値が返るようになっています。Doubleの方は同じ挙動のままでした。