Problem case

Let’s do a simpler example

public static void main(String[] args) { for (int i = 0; i < 150; i++) { Integer a = i; Integer b = i; System.out.println(i + " " + (a == b)); }}Copy the code

The value of I ranges from 0 to 150, and the values of A and B are the same each time, and the output a == b. Running results:

0 true
1 true
2 true
3 true
...
126 true
127 true
128 false
129 false
130 false
...
Copy the code

A and B are no longer equal starting at 128.

Cause analysis,

Let’s start with a review of automatic boxing. For the following line of code

Integer a = 1;
Copy the code

The variable a is of type Integer and 1 is of type int, and there is no inheritance relationship between Integer and int. This line of code should report an error according to normal Java handling.

However, due to automatic boxing, Java automatically converts int to Integer when assigning an int value to an Integer variable, i.e

Integer a = Integer.valueOf(1);
Copy the code

The valueOf() method returns a valueOf type Integer and assigns it to variable a. This is the autoboxing of int.

Let’s look at the first example:

public static void main(String[] args) { for (int i = 0; i < 150; i++) { Integer a = i; Integer b = i; System.out.println(i + " " + (a == b)); }}Copy the code

On each loop, Integer a = I and Integer b = I trigger automatic boxing, which converts int to an Integer value and returns it; We know that two new objects in Java because they are different instances, == will return Fasle anyway. Such as

new Integer(1) == new Integer(1);
Copy the code

Will return false.

In this example, Integer a = I and Integer b = I should not be the same object, so the result of == should be false. False above 128 is easy to understand, but why does it return true from 0 to 127? == return true only if the two objects being compared are the same object.

for(int i=0; i<150; i++){ Integer a=i; Integer b=i; System.out.println(a+" "+b+" "+System.identityHashCode(a)+" "+System.identityHashCode(b)); }Copy the code

The identityHashCode() method can be interpreted as printing the memory address of the corresponding variable.

0 0 762119098 762119098 11 1278349992 1278349992 2 2 1801910956 1801910956 3 3 1468253089 1468253089... 126 126 1605164995 1605164995 127 127 1318497351 1318497351 128 128 101224864 479240824 129 129 1373088356 636728630 130 130 587071409 1369296745...Copy the code

Autoboxing from 0 to 127 gives you the same object! Starting at 128 is normal.

Source code analysis

There is only one interpretation of “auto-boxing produces the same object from 0 to 127 different times” : auto-boxing does not necessarily create new objects.

Since the auto-boxing method involved is integer.valueof (), take a look at the source code:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
Copy the code

The comment directly states that values between -128 and 127 are taken directly from the cache. If int I is in the range IntegerCache. Low and IntegerCache. High, it is returned directly from IntegerCache. Otherwise new returns a new object. Low is -128, and high is 127

IntegerCache

private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue ! = null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) Assert IntegerCache. High >= 127; } private IntegerCache() {} }Copy the code

The static block generates Integer variables of type -128 to 127 and stores them in cache[]. For ints of type -128 to 127, the same Integer object is returned.

When IntegerCache is loaded (the Java VIRTUAL machine starts), the static block of IntegerCache’s internal type starts executing, instantiating and temporarily storing objects of Integer type between -128 and 127. When an int value between -128 and 127 is automatically boxed, the Integer temporarily stored in IntegerCache is returned

The solution

Since our goal is to compare values for equality, not for the same object; Since autoboxing does not guarantee that integers of the same value are the same object or not, use equals() instead of ==. In fact, Integer overrides the equals() method to directly compare the values of objects.

for (int i = 0; i < 150; i++) { Integer a = i; Integer b = i; System.out.println(i + " " + (a.equals(b))); } // return all true. private final int value; public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; } public int intValue() { return value; }Copy the code

note

Not only ints, but the other seven basic types in Java can be automatically boxed and unboxed, as well as being useful for caching. See the table below:

Basic types of Packing type Value range Whether the cache The cache scope
byte Byte – 128 ~ 127 is – 128 ~ 127
short Short -2^15 ~ (2^15-1) is – 128 ~ 127
int Integer -2^31 ~ (2^ 31-1) is – 128 ~ 127
long Long -2^63 ~ (2^63-1) is – 128 ~ 127
float Float no
double Double no
boolean Boolean true, false is true, false
char Character \u0000 ~ \uffff