I’ve discovered a strange behavior for mutable sets which I cannot understand:
I have a object which I want to add to a set. The equals method for the class is overridden. When I add two different objects to the set, which produces the same output for equals method, I get a different behavior between mutable and immutable sets for the contains method.
Here is the code snippet:
class Test(text:String){
override def equals(obj:Any) = obj match {
case t: Test => if (t.text == this.text) true else false
case _ => false
}
override def toString = text
}
val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty
mutableSet += new Test("test")
println(mutableSet)
println(mutableSet.contains(new Test("test")))
val immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty
immutableSet += new Test("test")
println(immutableSet)
println(immutableSet.contains(new Test("test")))
This produces as output:
Set(test)
false
Set(test)
true
In my opinion both calls of contains should produce the same output (true).
Could anybody help me to understand the difference here or is this a bug in the scala immutable set implementation? By the way, I use scala 2.8.1.final
Thanks.
Rule 1 when implementing equals(): Implement hashCode() at the same time. See Overriding equals and hashCode in Java
In the first example, you’re creating a mutable set, which calls hashCode to set up the hash table.
In the second, you’re using an immutable set with one entry, so Scala actually uses an optimised version of Set called Set1. Set1.contains() just compares the one entry with the passed element using equals() directly. This looks like:
No hashCode is called. There is also a Set2, Set3 and Set4.
So if we change your code to be:
adding a hashCode and a println in the equals, and the output is:
which explains why the mutable.contains() isn’t working correctly. It is looking up the object in the wrong hash table entry, equals() doesn’t even get called. And, unsurprisingly, it doesn’t find it.
You can implement hashCode using text.hashCode: