0

In this piece of code

object Program1 extends App {

  val set = scala.collection.mutable.Set( "Abc", "def")
  set += "ghi"

}

since set is mutable in this case means it will add the elements to itself and no new set will be created but when I tried to display hashCode() of the set like this :

object Program1 extends App {

 val set = scala.collection.mutable.Set( "Abc", "def")
 println(set.hashCode)
 set += "ghi"
 println(set.hashCode)

}

I expected that println() statements will output the same hashcode but it printed different, as set object is not changed and we are just appending to the existing set then why hashcode is comming different.

Aamir
  • 2,139
  • 1
  • 18
  • 47

2 Answers2

3

If you look at hashCode implementation of a mutable HashSet in scala, you'll find out that it hashes all its internal elements. My scala version ends up in MurmurHash3.unorderedHash(...) call when hashing. Probably can be different at others.

If you ask why? I guess that's because Set("Abc", "def") != Set("Abc", "def", "ghi") and also to be aligned with immutable HashSet implementation. It makes perfect sense and I have no idea why you would do otherwise.

Update

Some additional explanation to answer author's comments

hashCode is all about equality, not about the same object. The rule is that if two objects are equal, then they should return the same hashCode. If they are not - they better return different (I say better, because its still possible they return the same by collision). This is true for all objects.

Consider this code:

import scala.collection.mutable.Set

val s1 = Set(1)
val superSet = Set(s1)
println(superSet.contains(s1)) // prints true
println(superSet.contains(Set(1)) // still prints true

Notice that both prints true, even though s1 and Set(1) are two different objects in memory. This is because their equals and hashCode returns the same

Now there is a small problem with this implementation:

import scala.collection.mutable.Set

val superMap = scala.collection.mutable.Map.empty[Set[String], Boolean]

superMap += (set -> true)
println(superMap.contains(set)) // prints true

set += "ghi"
println(superMap.contains(set)) // prints false
println(superMap.contains(Set("Abc", "def"))) // still prints false

The second println prints false. This is because Map can no longer find the key, because key has changed its hashCode, but Map still remembers the old one. The third println still fails the check, because even though the contains hashCode in Map and Set("Abc", "def").hashCode are the same, the sets fail the equality check afterwards.

This is a well-known problem and there is no good solution, so it is recommended to never use mutable objects as the keys to HashMap. In general you should not use mutable objects for any structure where hashCode-check and then equality-check is applied. The same thing holds for HashSet

Archeg
  • 7,785
  • 5
  • 34
  • 81
  • but set object is the same even after adding new element to it – Aamir Dec 19 '15 at 03:54
  • i.e set is still pointing to the same memory location even after adding new elements to it. – Aamir Dec 19 '15 at 04:02
  • actually i have this doubt like if ur set ref-variable is pointing to an object inside memory with some address, now when u append new element to it , will it point to the same memory locaton. – Aamir Dec 19 '15 at 09:42
  • If its mutable set - it will. In case of immutable, a new set will be created. And you should remember that despite set itself is located in some point of memory, it's objects in most of the cases are located in another part of memory, not inside it. If you were trying to track down the memory alignment with `hashCode` - its wrong and it won't give you proper measurements as `hashCode` is usually overrided – Archeg Dec 19 '15 at 09:45
  • You could try use https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#identityHashCode(java.lang.Object) but I'm actually not sure how reliable this is – Archeg Dec 19 '15 at 09:48
  • @Aamir np. Just a small clarification: I was wrong with statement `it will`. Not always. In fact JVM is allowed to move objects in memory anytime it wants, so you can never know. – Archeg Dec 19 '15 at 10:08
2

This functionality mirrors that which is recommended by all hashCode() and equals() implementations: if a1.equals(a2) then a1.hashCode() == a2.hashCode(). Take a look at the post here for more information https://stackoverflow.com/a/256447/1154145

Community
  • 1
  • 1
nattyddubbs
  • 2,065
  • 14
  • 22