- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers

# == and equals

Sun, 2010-06-20, 01:18

Hi

I have a very strange effect with == and equals. As far as I understand

should both methods give the same result for objects that are not null.

I will paste a session with the interactive shell from today:

----------------------------

scala> val a = 1

a: Int = 1

scala> val b = 1.0

b: Double = 1.0

scala> a == b

res90: Boolean = true

scala> b == a

res91: Boolean = true

scala> a equals b

res92: Boolean = false

scala> b equals a

res93: Boolean = false

----------------------------

Am I wrong or is there something wrong?

Best regards

Uwe Schirmer

Uwe Schirmer

Software Engineer and Author

E-Mail: uwe [at] schirmer [dot] org

Sun, 2010-06-20, 01:47

#2
Re: == and equals

FYI here is a summary of ==/equals I sent to -internals on Apr 13. I

didn't get a response on this but I'm pretty sure it's in the ballpark

of accurate since I implemented most of it.

From: Paul Phillips

To: martin odersky

Cc: scala-internals [at] listes [dot] epfl [dot] ch

Here is a kind of off the top of my head attempt to spec out equality

and hash codes. I don't really speak spec-ese but this is written in

the pidgin spec-ese within my grasp. Does this look approximately

correct? (Anyone else feel free to chime in on that point.) What if

anything would you like me to do with it?

Resolution of x == y

====================

1) Null values will not cause NPEs.

2) Nothing is == to null except null.

3) All objects must be == to themselves.

The first three conditions are summarized in this initial expansion of 'x == y', which the compiler may or may not inline. All user-defined equals methods are responsible for preserving invariants 2 and 3.

if (x eq y) true

else if (x eq null) false

else // remainder of algorithm

4) If the static type of the left hand side allows for the possibility that it is a boxed or unboxed primitive numeric type (any of Byte, Short, Int, Long, Float, Double, or Char) then: go to step 5.

If the static type definitively excludes those types, then: the result is x.equals(y).

5) If the static types of both operands are primitive types, then: the result is that of the primitive comparison, exactly as performed in java.

If the static types are identical final types (for instance, both are java.lang.Longs) then the result is x.equals(y).

In all other cases, both operands are boxed if necessary and a method in BoxesRunTime is called. (The method will be semantically equivalent to BoxesRunTime.equals, but a different method may be chosen to avoid repeating the above tests.)

BoxesRuntime.equals

===================

All of the preceding logic is preserved, and then it proceeds as follows, where 'x' remains the left hand side operand and 'y' the right.

1) Runtime instance checks will be done to determine the types of the operands, with the following resolutions. (Resolutions represent the semantics, not necessarily the implementation.)

1a) If both sides of the comparison are boxed primitives, then they are unboxed and the primitive comparison is performed as in java.

1b) If 'x' is a class implementing the scala.math.ScalaNumber trait, then the result is x.equals(y).

1c) If 'x' is a boxed primitive and 'y' is a class implementing the scala.math.ScalaNumber trait, then the result is y.equals(x).

1d) Otherwise, the result is x.equals(y).

hashCode and ##

===============

The unification of primitives and boxed types in scala necessitates measures to preserve the equality contract: equal objects must have equal hash codes. To accomplish this a new method is introduced on Any:

def ##: Int

This method should be called in preference to hashCode by all scala software which consumes hashCodes. (One need not use or even be aware of it unless implementing something which depends on hashCodes -- to define an object's hashCode, overridding hashCode remains the mechanism.)

The default implementation of ## is simply to call hashCode:

def ##: Int = this.hashCode()

In the case of numeric types however, it selectively alters hash codes to support the == algorithm given above. The guarantees provided by ## are as follows. "Numbers" are the aforementioned primitives (boxed or unboxed) and any standard scala library class implementing ScalaNumber.

1) If x and y are whole Numbers in the range Int.MinValue to Int.MaxValue, then (x == y) implies (x.## == y.##). The value of ## for all Numbers in that range is equal to the result of .toInt on that Number.

2) If x and y are Numbers and either or both is fractional, then the guarantee in 1) applies if both are in the range Short.MinValue to Short.MaxValue.

3) If x and y are Numbers and neither 1) nor 2) applies, the implication is preserved on a best-effort basis, but cannot be preserved generally given the fuzziness introduced in primitive equality at the borders. (For instance given a large Float, a java primitive Float/Double comparison may return true for 2^10 different Double values, and similar issues arise with Longs and Doubles.)

Sun, 2010-06-20, 07:57

#3
Re: == and equals

Thank you, you are great.

I had a look in some of the Books about Scala and all stated that == is

a test for null and calling equals.

MAny thanks

Uwe

On Sat, 19 Jun 2010 17:33 -0700, "Paul Phillips"

wrote:

> FYI here is a summary of ==/equals I sent to -internals on Apr 13. I

> didn't get a response on this but I'm pretty sure it's in the ballpark

> of accurate since I implemented most of it.

>

>

> From: Paul Phillips

> To: martin odersky

> Cc: scala-internals [at] listes [dot] epfl [dot] ch

>

> Here is a kind of off the top of my head attempt to spec out equality

> and hash codes. I don't really speak spec-ese but this is written in

> the pidgin spec-ese within my grasp. Does this look approximately

> correct? (Anyone else feel free to chime in on that point.) What if

> anything would you like me to do with it?

>

> Resolution of x == y

> ====================

>

> 1) Null values will not cause NPEs.

>

> 2) Nothing is == to null except null.

>

> 3) All objects must be == to themselves.

>

> The first three conditions are summarized in this initial expansion of 'x

> == y', which the compiler may or may not inline. All user-defined equals

> methods are responsible for preserving invariants 2 and 3.

>

> if (x eq y) true

> else if (x eq null) false

> else // remainder of algorithm

>

> 4) If the static type of the left hand side allows for the possibility

> that it is a boxed or unboxed primitive numeric type (any of Byte, Short,

> Int, Long, Float, Double, or Char) then: go to step 5.

>

> If the static type definitively excludes those types, then: the result is

> x.equals(y).

>

> 5) If the static types of both operands are primitive types, then: the

> result is that of the primitive comparison, exactly as performed in java.

>

> If the static types are identical final types (for instance, both are

> java.lang.Longs) then the result is x.equals(y).

>

> In all other cases, both operands are boxed if necessary and a method in

> BoxesRunTime is called. (The method will be semantically equivalent to

> BoxesRunTime.equals, but a different method may be chosen to avoid

> repeating the above tests.)

>

> BoxesRuntime.equals

> ===================

>

> All of the preceding logic is preserved, and then it proceeds as follows,

> where 'x' remains the left hand side operand and 'y' the right.

>

> 1) Runtime instance checks will be done to determine the types of the

> operands, with the following resolutions. (Resolutions represent the

> semantics, not necessarily the implementation.)

>

> 1a) If both sides of the comparison are boxed primitives, then they are

> unboxed and the primitive comparison is performed as in java.

>

> 1b) If 'x' is a class implementing the scala.math.ScalaNumber trait, then

> the result is x.equals(y).

>

> 1c) If 'x' is a boxed primitive and 'y' is a class implementing the

> scala.math.ScalaNumber trait, then the result is y.equals(x).

>

> 1d) Otherwise, the result is x.equals(y).

>

> hashCode and ##

> ===============

>

> The unification of primitives and boxed types in scala necessitates

> measures to preserve the equality contract: equal objects must have equal

> hash codes. To accomplish this a new method is introduced on Any:

>

> def ##: Int

>

> This method should be called in preference to hashCode by all scala

> software which consumes hashCodes. (One need not use or even be aware of

> it unless implementing something which depends on hashCodes -- to define

> an object's hashCode, overridding hashCode remains the mechanism.)

>

> The default implementation of ## is simply to call hashCode:

>

> def ##: Int = this.hashCode()

>

> In the case of numeric types however, it selectively alters hash codes to

> support the == algorithm given above. The guarantees provided by ## are

> as follows. "Numbers" are the aforementioned primitives (boxed or

> unboxed) and any standard scala library class implementing ScalaNumber.

>

> 1) If x and y are whole Numbers in the range Int.MinValue to

> Int.MaxValue, then (x == y) implies (x.## == y.##). The value of ## for

> all Numbers in that range is equal to the result of .toInt on that

> Number.

>

> 2) If x and y are Numbers and either or both is fractional, then the

> guarantee in 1) applies if both are in the range Short.MinValue to

> Short.MaxValue.

>

> 3) If x and y are Numbers and neither 1) nor 2) applies, the implication

> is preserved on a best-effort basis, but cannot be preserved generally

> given the fuzziness introduced in primitive equality at the borders.

> (For instance given a large Float, a java primitive Float/Double

> comparison may return true for 2^10 different Double values, and similar

> issues arise with Longs and Doubles.)

>

Sun, 2010-06-20, 10:07

#4
Re: == and equals

Just for the record. This is correct code !?

Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java

1.6.0_20).

Type in expressions to have them evaluated.

Type :help for more information.

scala> val no= Array.ofDim(1)

no: Array[Nothing] = Array(null)

scala> println(no(0)==null)

true

regards

esser

Sun, 2010-06-20, 10:37

#5
Re: Re: == and equals

On Sun, Jun 20, 2010 at 02:02:10AM -0700, friedrich wrote:

> Just for the record. This is correct code !?

> scala> val no= Array.ofDim(1)

> no: Array[Nothing] = Array(null)

Nothing should not be inhabited, even by null. This is a compiler bug.

> scala> println(no(0)==null)

> true

Apart from the above, this is OK.

Lauri

Mon, 2010-06-21, 03:57

#6
Re: == and equals

Paul Phillips-3 wrote:

>

> Resolution of x == y

> ====================

>

> 1) Null values will not cause NPEs.

>

> 2) Nothing is == to null except null.

>

> 3) All objects must be == to themselves.

>

> The first three conditions are summarized in this initial expansion of 'x

> == y', which the compiler may or may not inline. All user-defined equals

> methods are responsible for preserving invariants 2 and 3.

>

> if (x eq y) true

> else if (x eq null) false

> else // remainder of algorithm

>

If the left-hand side is a null with a static type compatible with

java.lang.Number and the right-hand side is a primitive, then I get an NPE.

This contradicts the first rule above. Is this the expected behavior? The

below is from 2.8.0.RC6:

scala> null == 42

res0: Boolean = false

scala> (null:java.lang.Integer) == 42

java.lang.NullPointerException

at scala.runtime.BoxesRunTime.equalsNumNum(Unknown Source)

at scala.runtime.BoxesRunTime.equalsNumObject(Unknown Source)

scala> (null:java.lang.Integer) == new java.lang.Integer(42)

res2: Boolean = false

scala> (null:String) == 42

res3: Boolean = false

scala> (null:java.lang.Integer) == "foo"

res4: Boolean = false

Thanks,

Ross

Mon, 2010-06-21, 04:07

#7
Re: Re: == and equals

On Sun, Jun 20, 2010 at 07:54:44PM -0700, Ross A. Baker wrote:

> If the left-hand side is a null with a static type compatible with

> java.lang.Number and the right-hand side is a primitive, then I get an

> NPE. This contradicts the first rule above. Is this the expected

> behavior?

Very much a bug, which I will fix immediately.

On Sun, Jun 20, 2010 at 02:18:04AM +0200, uwe [at] scala-info [dot] org wrote:

> I have a very strange effect with == and equals. As far as I

> understand should both methods give the same result for objects that

> are not null.

...except primitives.

> Am I wrong or is there something wrong?

You are incorrect. I don't know if the spec has been updated. With the

exception of a couple corner case issues, comparing two primitives

(boxed or unboxed) with == should always give the result you would have

gotten by comparing those values as unboxed primitives. When you call

equals directly, you are skipping all that softening logic and instead

treated to java's theory that two boxed values of different types are

always unequal.