Alex
Alex

Reputation: 2976

Scala: why do my case class instances have the same hashCode?

I am using Scala 2.11.

I have a case class Dimension and I created 3 instances of it. When I put them into a HashSet, I surprisingly found that only 1 was added properly. Then I tried to debug and found out they had the same hashCode.

I am new to Scala, but have a lot of experience in Java. I am wondering why all of them are having the same hashCode even if they have different fields and what is the default implementation of hashCode method in Scala's case class? And how does HashSet/HashMap work in Scala?

Here is my code example.

object Echo {
  def main( args:Array[String] ):Unit = {
    var d1 = new Dimension
    d1.name = "d1"
    d1.dimensionId = "1"
    println("d1:" + d1.hashCode()) // d1, d2, d3 have the same hashCode

    var d2 = new Dimension
    d2.name = "d2"
    d2.dimensionId = "2"
    println("d2:" + d2.hashCode())

    var d3 = new Dimension
    d3.name = "d3"
    d3.dimensionId = "3"
    println("d3:" + d3.hashCode())

    var l = List(d1, d2, d3)
    val categories = mutable.HashSet.empty[Dimension]

    l.foreach(md => {
      categories += md
    })

    println(categories.size) // size is 1
  }
}

case class Dimension() {
  var dimensionId: String = _
  var name: String = _
}

Upvotes: 1

Views: 1775

Answers (3)

Dennis Hunziker
Dennis Hunziker

Reputation: 1293

As pointed out, hashCode uses primary constructor args to generate its result. You can still have a working version of your case class using vars though, like:

case class Dimension(var dimensionId: String = "", var name: String = "")

Upvotes: 2

Andrey Tyukin
Andrey Tyukin

Reputation: 44918

Quoting the spec:

Every case class implicitly overrides some method definitions of class scala.AnyRef unless a definition of the same method is already given in the case class itself or a concrete definition of the same method is given in some base class of the case class different from AnyRef. In particular:

  • Method equals: (Any)Boolean is structural equality, where two instances are equal if they both belong to the case class in question and they have equal (with respect to equals) constructor arguments (restricted to the class's elements, i.e., the first parameter section).

  • Method hashCode: Int computes a hash-code. If the hashCode methods of the data structure members map equal (with respect to equals) values to equal hash-codes, then the case class hashCode method does too.

Since the argument list of the constructor is empty, every Dimension vacuously equals any other Dimension, therefore their hashCode must also be the same.

Just don't mix case classes with those weird uninitialized vars, or at least do it more carefully.

Upvotes: 6

SCouto
SCouto

Reputation: 7928

HashCode in scala only considers attributes in the constructor for case classes.

If you define your case class in a more functional and scalish way (assuring inmutabilitity for instance) the behaviour will be the expected:

DEFINING

case class Dimension(dimensionId: String, name: String)
val d1 = Dimension("1", "d1")
val d2 = Dimension("2", "d2")

RESULT

  scala> println("d1:" + d1.hashCode())
    d1:732406741
    scala> println("d2:" + d2.hashCode())
    d2:952021182

You can find the generated code for the hashcode method in this awesome answer here

Upvotes: 5

Related Questions