Reputation: 2976
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
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
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 var
s, or at least do it more carefully.
Upvotes: 6
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