データ構造をMapなどに格納する場合、hashCode
とequals
を適切に定義しないと、keyによる検索が上手くいかない。
object Point {
def apply(x:Int, y:Int) = new Point(x, y)
}
class Point(val x:Int, val y:Int) {
// Add and multiply by prime numbers
override def hashCode = (x + 31) * 31 + y
override def equals(other:Any) = other match {
case that: Point =>
(that canEqual this) && (this.x == that.x) && (this.y == that.y)
case _ => false
}
// Pointを継承した他のクラスのインスタンスでないかチェック
def canEqual(other:Any) = other.isInstanceOf[Point]
}
以上のようにhashCode, equalsの定義をするとMapに格納されたkeyを検索できるようになる。
val m = Map(Point(1, 3) -> "A", Point(5, 5) -> "B")
val v = m(Point(1,3)) // "A" が見つかる
また、canEqual
の部分でのチェックは、例えば以下のようにPointを拡張したPointWithColorを作成した場合、PointクラスのインスタンスとPointWithColorのインスタンスを誤って同一視しないために必要。
class PointWithColor(x:Int, y:Int, val color:String) extends Point(x, y)
もし上のコードからcanEqual
のチェック部分を取り除くと、
val p = new Point(1, 1)
val c = new PointWithColor(1, 1, "red")
p == c // trueになってしまう!!
==
では、equalsのメソッドを用いてオブジェクトの比較がなされるが、eq
は参照としての比較がなされる。参照先が同じインスタンスを指す場合、eq
による比較はtrueを返す。
scala> val p1 = new Point(1, 1)
p1: Point = Point@6bb
scala> val p2 = new Point(1, 1)
p2: Point = Point@6bb
scala> p1 == p2
res37: Boolean = true
scala> p1 eq p2
res38: Boolean = false
scala> val p3 = p1
p3: Point = Point@6bb
scala> p3 eq p1
res39: Boolean = true
より良いhash関数の計算に関してはUniversal hashingを参照のこと。