@@ -60,15 +60,16 @@ private[hashing] class MurmurHash3 {
60
60
finalizeHash(h, 2 )
61
61
}
62
62
63
- /** Compute the hash of a product */
63
+ // @deprecated("use `caseClassHash` instead", "2.13.17")
64
+ // The deprecation is commented because this method is called by the synthetic case class hashCode.
65
+ // In this case, the `seed` already has the case class name mixed in and `ignorePrefix` is set to true.
66
+ // Case classes compiled before 2.13.17 call this method with `productSeed` and `ignorePrefix = false`.
67
+ // See `productHashCode` in `SyntheticMethods` for details.
64
68
final def productHash (x : Product , seed : Int , ignorePrefix : Boolean = false ): Int = {
65
69
val arr = x.productArity
66
- // Case objects have the hashCode inlined directly into the
67
- // synthetic hashCode method, but this method should still give
68
- // a correct result if passed a case object.
69
- if (arr == 0 ) {
70
- x.productPrefix.hashCode
71
- } else {
70
+ if (arr == 0 )
71
+ if (! ignorePrefix) x.productPrefix.hashCode else seed
72
+ else {
72
73
var h = seed
73
74
if (! ignorePrefix) h = mix(h, x.productPrefix.hashCode)
74
75
var i = 0
@@ -80,6 +81,24 @@ private[hashing] class MurmurHash3 {
80
81
}
81
82
}
82
83
84
+ /** See the [[MurmurHash3.caseClassHash(x:Product,caseClassName:String) ]] overload */
85
+ final def caseClassHash (x : Product , seed : Int , caseClassName : String ): Int = {
86
+ val arr = x.productArity
87
+ val aye = (if (caseClassName != null ) caseClassName else x.productPrefix).hashCode
88
+ if (arr == 0 ) aye
89
+ else {
90
+ var h = seed
91
+ h = mix(h, aye)
92
+ var i = 0
93
+ while (i < arr) {
94
+ h = mix(h, x.productElement(i).## )
95
+ i += 1
96
+ }
97
+ finalizeHash(h, arr)
98
+ }
99
+ }
100
+
101
+
83
102
/** Compute the hash of a string */
84
103
final def stringHash (str : String , seed : Int ): Int = {
85
104
var h = seed
@@ -337,14 +356,46 @@ object MurmurHash3 extends MurmurHash3 {
337
356
final val mapSeed = " Map" .hashCode
338
357
final val setSeed = " Set" .hashCode
339
358
340
- def arrayHash [@ specialized T ](a : Array [T ]): Int = arrayHash(a, arraySeed)
341
- def bytesHash (data : Array [Byte ]): Int = bytesHash(data, arraySeed)
342
- def orderedHash (xs : IterableOnce [Any ]): Int = orderedHash(xs, symmetricSeed)
343
- def productHash (x : Product ): Int = productHash(x, productSeed)
344
- def stringHash (x : String ): Int = stringHash(x, stringSeed)
345
- def unorderedHash (xs : IterableOnce [Any ]): Int = unorderedHash(xs, traversableSeed)
359
+ def arrayHash [@ specialized T ](a : Array [T ]): Int = arrayHash(a, arraySeed)
360
+ def bytesHash (data : Array [Byte ]): Int = bytesHash(data, arraySeed)
361
+ def orderedHash (xs : IterableOnce [Any ]): Int = orderedHash(xs, symmetricSeed)
362
+ def stringHash (x : String ): Int = stringHash(x, stringSeed)
363
+ def unorderedHash (xs : IterableOnce [Any ]): Int = unorderedHash(xs, traversableSeed)
346
364
def rangeHash (start : Int , step : Int , last : Int ): Int = rangeHash(start, step, last, seqSeed)
347
365
366
+ @ deprecated(" use `caseClassHash` instead" , " 2.13.17" )
367
+ def productHash (x : Product ): Int = caseClassHash(x, productSeed, null )
368
+
369
+ /**
370
+ * Compute the `hashCode` of a case class instance. This method returns the same value as `x.hashCode`
371
+ * if `x` is an instance of a case class with the default, synthetic `hashCode`.
372
+ *
373
+ * This method can be used to implement case classes with a cached `hashCode`:
374
+ * {{{
375
+ * case class C(data: Data) {
376
+ * override lazy val hashCode: Int = MurmurHash3.caseClassHash(this)
377
+ * }
378
+ * }}}
379
+ *
380
+ * '''NOTE''': For case classes (or subclasses) that override `productPrefix`, the `caseClassName` parameter
381
+ * needs to be specified in order to obtain the same result as the synthetic `hashCode`. Otherwise, the value
382
+ * is not in sync with the case class `equals` method (scala/bug#13033).
383
+ *
384
+ * {{{
385
+ * scala> case class C(x: Int) { override def productPrefix = "Y" }
386
+ *
387
+ * scala> C(1).hashCode
388
+ * val res0: Int = -668012062
389
+ *
390
+ * scala> MurmurHash3.caseClassHash(C(1))
391
+ * val res1: Int = 1015658380
392
+ *
393
+ * scala> MurmurHash3.caseClassHash(C(1), "C")
394
+ * val res2: Int = -668012062
395
+ * }}}
396
+ */
397
+ def caseClassHash (x : Product , caseClassName : String = null ): Int = caseClassHash(x, productSeed, caseClassName)
398
+
348
399
private [scala] def arraySeqHash [@ specialized T ](a : Array [T ]): Int = arrayHash(a, seqSeed)
349
400
private [scala] def tuple2Hash (x : Any , y : Any ): Int = tuple2Hash(x.## , y.## , productSeed)
350
401
@@ -397,8 +448,13 @@ object MurmurHash3 extends MurmurHash3 {
397
448
def hash (xs : IterableOnce [Any ]) = orderedHash(xs)
398
449
}
399
450
451
+ @ deprecated(" use `caseClassHashing` instead" , " 2.13.17" )
400
452
def productHashing = new Hashing [Product ] {
401
- def hash (x : Product ) = productHash(x)
453
+ def hash (x : Product ) = caseClassHash(x)
454
+ }
455
+
456
+ def caseClassHashing = new Hashing [Product ] {
457
+ def hash (x : Product ) = caseClassHash(x)
402
458
}
403
459
404
460
def stringHashing = new Hashing [String ] {
0 commit comments