Scala in Depth presents this code on mutability and equality.
class Point2(var x: Int, var y: Int) extends Equals {
def move(mx: Int, my: Int) : Unit = {
x = x + mx
y = y + my
}
override def hashCode(): Int = y + (31*x)
def canEqual(that: Any): Boolean = that match {
case p: Point2 => true
case _ => false
}
override def equals(that: Any): Boolean = {
def strictEquals(other: Point2) =
this.x == other.x && this.y == other.y
that match {
case a: AnyRef if this eq a => true
case p: Point2 => (p canEqual this) && strictEquals(p)
case _ => false
}
}
}
Then, it performs evaluations.
scala> val x = new Point2(1,1)
x: Point2 = Point2@20
scala> val y = new Point2(1,2)
y: Point2 = Point2@21
scala> val z = new Point2(1,1)
z: Point2 = Point2@20
Next, a HashMap is created.
scala> val map = HashMap(x -> "HAI", y -> "WORLD")
map: scala.collection.immutable.HashMap[Point2,java.lang.String] =
Map((Point2@21,WORLD), (Point2@20,HAI))
scala> x.move(1,1)
scala> map(y)
res9: java.lang.String = WORLD
I understand that map(x) will return a NoSuchElementException since x has mutated. x's hashCode gets re-computed due to the mutation of x.move(1,1). As a result, when checking if x is in the map, none of map's hashCodes match x's new hashCode.
scala> map(x)
java.util.NoSuchElementException: key not found: Point2@40
...
Since z equals (value) the originally inserted x of HashMap, as well as the hashCode, why is the exception thrown?
scala> map(z)
java.util.NoSuchElementException: key not found: Point2@20
EDIT This example, in my opinion, shows the complexity (bad) of imperative programming.
Because the Map still uses x to test for equality.
Here is what happens:
x as a key, the hashCode at this time is #x. Great.x, #x is now gone, the new hashCode is #x'.x in the map. The map gets the hashCode: #x'. It does not exist in the map (since at the time of insertion it was #x).z with the same values x originally had.z. The map found a value for the hashCode of z (since it's #x), but then calls equals on z and x (the same instance you used to insert the value during the first step). And you get false since you moved x! The map keeps a reference to the instance of the key, and use it to test equals when you get, but it never re-computes the hashCode.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With