43

What is the difference between methods ## and hashCode?

They seem to be outputting the same values no matter which class or hashCode overloading I use. Google doesn't help, either, as it cannot find symbol ##.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0__
  • 66,707
  • 21
  • 171
  • 266
  • 1
    `1.0 hashCode` v `1.0 ##` v `1 hashCode` v `1 ##` — http://www.scala-lang.org/api/current/scala/Any.html – Debilski Jan 30 '12 at 17:50
  • 5
    A little offtopic, but you can search for such symbols using [SymbolHound](http://www.symbolhound.com/). – om-nom-nom Jan 30 '12 at 18:10
  • 2
    Ah ok. So, `1.hashCode` `==` `1.##`, and `1.2.hashCode` `==` `1.2.##`. The only thing that behaves differently is `1.0.hashCode` `!=` `1.0.##` (so `##` is better suited for comparing numbers). – 0__ Jan 30 '12 at 18:14
  • 1
    From the Scala docs on Any "Equivalent to x.hashCode except for boxed numeric types. For numerics, it returns a hash value which is consistent with value equality: if two value type instances compare as true, then ## will produce the same hash value for each of them.". – Brian Jan 30 '12 at 18:15
  • @om-nom-nom, a little of topic, but thanks for the tip. That's awesome! – Paul Draper Feb 19 '14 at 10:36

4 Answers4

39

"Subclasses" of AnyVal do not behave properly from a hashing perspective:

scala> 1.0.hashCode
res14: Int = 1072693248

Of course this is boxed to a call to:

scala> new java.lang.Double(1.0).hashCode
res16: Int = 1072693248

We might prefer it to be:

scala> new java.lang.Double(1.0).##
res17: Int = 1

scala> 1.0.##
res15: Int = 1

We should expect this given that the int 1 is also the double 1. Of course this issue does not arise in Java. Without it, we'd have this problem:

Set(1.0) contains 1 //compiles but is false

Luckily:

scala> Set(1.0) contains 1
res21: Boolean = true
oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • So, when implementing both `equals` and `hashCode` for a particular class, I'm not sure how this translates into the best strategy. I am assuming the `hashCode` implementation should use `##`. However, should the `equals` implementation use `hashCode` or `##`? Here's the answer I provided on another thread which assumes that the `equals` implementation should use `hashCode` and the `hashCode` implementation should use `##`. Evaluation and feedback on this would be greatly appreciated. https://stackoverflow.com/a/56509518/501113 – chaotic3quilibrium Jun 09 '19 at 19:14
35

## was introduced because hashCode is not consistent with the == operator in Scala. If a == b then a.## == b.## regardless of the type of a and b (if custom hashCode implementations are correct). The same is not true for hashCode as can be seen in the examples given by other posters.

Jesper Nordenberg
  • 2,104
  • 11
  • 15
  • So, when implementing both `equals` and `hashCode` for a particular class, I'm not sure how this translates into the best strategy. I am assuming the `hashCode` implementation should use `##`. However, should the `equals` implementation use `hashCode` or `##`? Here's the answer I provided on another thread which assumes that the `equals` implementation should use `hashCode` and the `hashCode` implementation should use `##`. Evaluation and feedback on this would be greatly appreciated. https://stackoverflow.com/a/56509518/501113 – chaotic3quilibrium Jun 09 '19 at 19:15
6

Just want to add to the answers of other posters that although the ## method strives to keep the contract between equality and hash codes, it is apparently not good enough in some cases, like when you are comparing doubles and longs (scala 2.10.2):

> import java.lang._
import java.lang._

> val lng = Integer.MAX_VALUE.toLong + 1
lng: Long = 2147483648

> val dbl = Integer.MAX_VALUE.toDouble + 1
dbl: Double = 2.147483648E9

> lng == dbl
res65: Boolean = true

> lng.## == dbl.##
res66: Boolean = false

> (lng.##, lng.hashCode)
res67: (Int, Int) = (-2147483647,-2147483648)

> (dbl.##, dbl.hashCode)
res68: (Int, Int) = (-2147483648,1105199104)
starling
  • 61
  • 1
  • 1
  • Really? Is this a bug? – Paul Draper Feb 18 '14 at 16:33
  • It clearly is as according to scaladoc for ## "... it returns a hash value which is consistent with value equality: if two value type instances compare as true, then ## will produce the same hash value for each of them..." – starling Feb 19 '14 at 09:29
  • 2
    I just checked this in scala 2.11.7 and did not see the same behavior for `==` and `.## == .##` – EdgeCaseBerg Dec 02 '16 at 16:05
3

In addition to what everyone else said, I'd like to say that ## is null-safe, because null.## returns 0 whereas null.hashCode throws NullPointerException.

From scaladoc:

Equivalent to x.hashCode except for boxed numeric types and null. For numerics, it returns a hash value which is consistent with value equality: if two value type instances compare as true, then ## will produce the same hash value for each of them. For null returns a hashcode where null.hashCode throws a NullPointerException.

pavel_orekhov
  • 1,657
  • 2
  • 15
  • 37