Frage

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.

War es hilfreich?

Lösung

Because the Map still uses x to test for equality.

Here is what happens:

  • you insert in the map using x as a key, the hashCode at this time is #x. Great.
  • you change some values on x, #x is now gone, the new hashCode is #x'.
  • you try to look up the value associated to 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).
  • you create z with the same values x originally had.
  • you look up the value associated to 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top