Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Java handle a long comparison between a primitive number and a reference number?

Tags:

java

I've tried the following source code on my laptop (Oracle HotSpot JVM, JDK 1.8, 64 bits):

Long l;
Long l1 = 100L;
Long l2 = 100L;
System.out.println(Long.valueOf(100L) == Long.valueOf(100L));
System.out.println((l = 100L) == Long.valueOf(100L));
System.out.println(l1 == l2);
System.out.println(Long.valueOf(128L) == Long.valueOf(128L));
System.out.println(Long.valueOf(129L) == 129L);
System.out.println(Long.valueOf(255L) == new Long(255L));

Then I decompiled the source code in IntelliJ IDEA Community 2019.1 to get following content:

Long l1 = 100L;
Long l2 = 100L;
System.out.println(100L == 100L);
System.out.println(100L == 100L);
System.out.println(l1 == l2);
System.out.println(128L == 128L);
System.out.println(Long.valueOf(129L) == 129L);
System.out.println(255L == new Long(255L));

And I've got answers:

true
true
true
false
true
false

I already know that integer assignment from a primitive number to the corresponding reference number will be automatically boxed. And the cache will be used if the number is in [-128,127], which means the results of lines 4, 5, 6, 7, 9 are reasonable.

However, I'm very curious about how Java handles the comparison between a reference number and a primitive number on line 8? In other words, how are primitive numbers actually stored in memory in Oracle HotSpot JVM?

I haven't found any help so far. Any suggestion will be appreciated.


Updated 2019-04-02 19:25:24: I've tried to show the addresses of Long.valueOf(129L) and 129L as follows:

System.out.println(System.identityHashCode(Long.valueOf(129L)));
System.out.println(System.identityHashCode(129L));
System.out.println(System.identityHashCode(129L));

I got the following information:

731260860
1709366259
1335298403

They are obviously different objects, even if it seems like they are the same primitive number, 129L.

like image 538
Gemini Keith Avatar asked Apr 02 '19 10:04

Gemini Keith


1 Answers

Any strange things about the decompilation of the .class file are probably not relevant. They could simply mean that the decompiler that you used is unreliable. So lets ignore your decompiled code.


The general rules when testing equality of primitive and boxed numeric types are as follows (simple version):

  • <primitive-number> == <primitive-number> :

    1. if the values are not the same type, convert the "smaller" to the type of the larger.
    2. compare using the ordering of the primitive type
  • <primitive-number> == <boxed-number> or <boxed-number> == <primitive-number>:

    1. unbox the boxed number to it corresponding primitive type
    2. if the values are not the same type, convert the "smaller" to the type of the larger.
    3. compare using the ordering of the primitive type
  • <boxed-number> == <boxed-number>

    1. if the boxed types are different: compilation error
    2. if the boxed types are the same, compare based on reference equality.

The relevant sections of the JLS 11 are 15.21.1 for the first two bullets and 15.21.3 for the third one. (You should find that all editions of the JLS from Java 5 onward say essentially the same thing.)

The complicating issue is that boxing the same primitive value twice may or may not give you the same reference. It depends on the type and the value and (in some circumstances) on JVM command line switches(!).

The types Byte, Short, Integer and Long all maintain a cache of boxed values for a subrange of their value space. Typically the subrange is from -128 to +127 inclusive, but this is not specified. If you autobox a primitive in that range, you will get the cached value. The same applies if you used <Type>.valueOf(<prim-type>) to convert a value in that range1. But if you call new <Type>(<prim-type>) you will *always get a newly created object.

The take away is that it is a bad idea to use == to compare two boxed numbers because the answer you get is often unpredictable.


1 - This is because boxing is specified to use the valueOf method.

like image 159
Stephen C Avatar answered Nov 16 '22 06:11

Stephen C