removes redundant hash implementation from BoxesRunTime.java #520

Merged
merged 2 commits into from May 9, 2012
@@ -203,67 +203,6 @@ private static boolean equalsNumChar(java.lang.Number xn, java.lang.Character yc
}
}
- /** Hashcode algorithm is driven by the requirements imposed
- * by primitive equality semantics, namely that equal objects
- * have equal hashCodes. The first priority are the integral/char
- * types, which already have the same hashCodes for the same
- * values except for Long. So Long's hashCode is altered to
- * conform to Int's for all values in Int's range.
- *
- * Float is problematic because it's far too small to hold
- * all the Ints, so for instance Int.MaxValue.toFloat claims
- * to be == to each of the largest 64 Ints. There is no way
- * to preserve equals/hashCode alignment without compromising
- * the hashCode distribution, so Floats are only guaranteed
- * to have the same hashCode for whole Floats in the range
- * Short.MinValue to Short.MaxValue (2^16 total.)
- *
- * Double has its hashCode altered to match the entire Int range,
- * but is not guaranteed beyond that. (But could/should it be?
- * The hashCode is only 32 bits so this is a more tractable
- * issue than Float's, but it might be better simply to exclude it.)
- *
- * Note: BigInt and BigDecimal, being arbitrary precision, could
- * be made consistent with all other types for the Int range, but
- * as yet have not.
- *
- * Note: Among primitives, Float.NaN != Float.NaN, but the boxed
- * verisons are equal. This still needs reconciliation.
- */
- public static int hashFromLong(java.lang.Long n) {
- int iv = n.intValue();
- if (iv == n.longValue()) return iv;
- else return n.hashCode();
- }
- public static int hashFromDouble(java.lang.Double n) {
- int iv = n.intValue();
- double dv = n.doubleValue();
- if (iv == dv) return iv;
-
- long lv = n.longValue();
- if (lv == dv) return java.lang.Long.valueOf(lv).hashCode();
- else return n.hashCode();
- }
- public static int hashFromFloat(java.lang.Float n) {
- int iv = n.intValue();
- float fv = n.floatValue();
- if (iv == fv) return iv;
-
- long lv = n.longValue();
- if (lv == fv) return java.lang.Long.valueOf(lv).hashCode();
- else return n.hashCode();
- }
- public static int hashFromNumber(java.lang.Number n) {
- if (n instanceof java.lang.Long) return hashFromLong((java.lang.Long)n);
- else if (n instanceof java.lang.Double) return hashFromDouble((java.lang.Double)n);
- else if (n instanceof java.lang.Float) return hashFromFloat((java.lang.Float)n);
- else return n.hashCode();
- }
- public static int hashFromObject(Object a) {
- if (a instanceof Number) return hashFromNumber((Number)a);
- else return a.hashCode();
- }
-
private static int unboxCharOrInt(Object arg1, int code) {
if (code == CHAR)
return ((java.lang.Character) arg1).charValue();
@@ -233,12 +233,39 @@ object ScalaRunTime {
//
// Note that these are the implementations called by ##, so they
// must not call ## themselves.
+ //
+ // Hashcode algorithm is driven by the requirements imposed
+ // by primitive equality semantics, namely that equal objects
+ // have equal hashCodes. The first priority are the integral/char
+ // types, which already have the same hashCodes for the same
+ // values except for Long. So Long's hashCode is altered to
+ // conform to Int's for all values in Int's range.
+ //
+ // Float is problematic because it's far too small to hold
+ // all the Ints, so for instance Int.MaxValue.toFloat claims
+ // to be == to each of the largest 64 Ints. There is no way
+ // to preserve equals/hashCode alignment without compromising
+ // the hashCode distribution, so Floats are only guaranteed
+ // to have the same hashCode for whole Floats in the range
+ // Short.MinValue to Short.MaxValue (2^16 total.)
+ //
+ // Double has its hashCode altered to match the entire Int range,
+ // but is not guaranteed beyond that. (But could/should it be?
+ // The hashCode is only 32 bits so this is a more tractable
+ // issue than Float's, but it might be better simply to exclude it.)
+ //
+ // Note: BigInt and BigDecimal, being arbitrary precision, could
+ // be made consistent with all other types for the Int range, but
+ // as yet have not.
+ //
+ // Note: Among primitives, Float.NaN != Float.NaN, but the boxed
+ // versions are equal. This still needs reconciliation.
@inline def hash(x: Any): Int = x match {
case null => 0
+ case x: Long => hash(x)
case x: Double => hash(x)
case x: Float => hash(x)
- case x: java.lang.Number => hash(x)
case _ => x.hashCode
}
@@ -266,7 +293,6 @@ object ScalaRunTime {
val high = (lv >>> 32).toInt
low ^ (high + lowSign)
}
- @inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x)
// The remaining overloads are here for completeness, but the compiler
// inlines these definitions directly so they're not generally used.
@@ -1,23 +1,23 @@
-// This only tests direct access to the methods in BoxesRunTime,
+// This only tests direct access to the methods in ScalaRunTime,
// not the whole scheme.
object Test
{
import java.{ lang => jl }
- import scala.runtime.BoxesRunTime.{ hashFromNumber, hashFromObject }
+ import scala.runtime.ScalaRunTime.{ hash }
def allSame[T](xs: List[T]) = assert(xs.distinct.size == 1, "failed: " + xs)
def mkNumbers(x: Int): List[Number] =
List(x.toByte, x.toShort, x, x.toLong, x.toFloat, x.toDouble)
- def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hashFromNumber)
+ def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hash)
def main(args: Array[String]): Unit = {
List(Byte.MinValue, -1, 0, 1, Byte.MaxValue) foreach { n =>
- val hashes = mkNumbers(n) map hashFromNumber
+ val hashes = mkNumbers(n) map hash
allSame(hashes)
if (n >= 0) {
- val charCode = hashFromObject(n.toChar: Character)
+ val charCode = hash(n.toChar: Character)
assert(charCode == hashes.head)
}
}
@@ -9,7 +9,15 @@ object Test {
val x = (BigInt(1) << 64).toDouble
val y: Any = x
+ val f: Float = x.toFloat
+ val jn: java.lang.Number = x
+ val jf: java.lang.Float = x.toFloat
+ val jd: java.lang.Double = x
assert(x.## == y.##, ((x, y)))
+ assert(x.## == f.##, ((x, f)))
+ assert(x.## == jn.##, ((x, jn)))
+ assert(x.## == jf.##, ((x, jf)))
+ assert(x.## == jd.##, ((x, jd)))
}
}