It’s actually more like a factor of two on a fair comparison. I did these tests when creating
AnyRefMap; switching from cooperative to non-cooperative equality (as possible when things are typed as
AnyRef) saves about a factor of two in speed. Using primitives directly gives about another factor of two (hence
LongMap), but that isn’t a fair comparison because we’re talking about the behavior of
Absolutely! The opaqueness of boxing of numbers is the source of endless Java puzzlers. Intuitively, the number one is the number one, regardless of whether it happens to be stored in a 32 bit integer or a 64 bit floating point value or a byte. It’s just one. Because users can create their own numeric type (e.g. Rational) with their own representation of one, it is not practical to maintain “one is one” universally. But it’s still a huge and worthwhile simplification of the cognitive model needed for dealing with numbers.
This is an implementation detail, presumably for speed. It needn’t be done this way. The various
equalsXYZ methods in scala.runtime can handle any comparison.
The current treatment is expensive, but makes numbers more regular than they would be otherwise, thus avoiding a class of bugs that people run into in Java.
Fundamentally, as long as we have weak conformance and such around numbers, it’s profoundly inconsistent to allow
1L + 1 but not say
1L == 1 is both valid and returns true.
Rust, for example, has decided to disallow all of these: you cannot write
1u64 + 1u32 or
1u64 == 1u32. This is consistent and reduces the chance of error, but is also something of a hassle. (Unadorned numeric literals will conform to the type expected to avoid making it much too much of a hassle.) But Rust has no top type, so there is no expectation that
(1L: Any) == (1: Any) behaves the same as
1L == 1.
So if cooperative equality were removed, I think equality on Any would have to go away entirely.