The comparison of primitive types and their wrapper classes (==) in Java has always been a headache. Not only are there automatic boxing and unboxing operations, but some of the wrapper classes also have object caching pools, which makes this part of knowledge confusing.

For the == operator, the values are compared if the data being compared are primitive types, and the memory addresses of objects are compared if they are objects. In addition, if one is a basic type and the other is a package type, the package type will be unpacked into the basic type before comparison, and then the comparison.

Taking int as an example, we divide the comparison types into three categories: int, Integer objects that are directly new, and Integer objects that are automatically boxed.

Leaving Integer’s cache pool aside, we permutations and combinations of pairwise comparisons between the three types. There are nine possible combinations of 3 * 3. Since the == operator is indifferent to left and right order, which means that a == b is equivalent to b == a, we can remove the three repeated comparisons, leaving only six cases. The details are as follows:

Type/type int new Integer Integer AutoBoxing
int 1. 2. 3.
new Integer 2. (4) 5.
Integer AutoBoxing 3. 5. 6.

Let’s take a look at each of the six cases, with the following code:

Situation (1) :

Int i1 = 128; int i2 = 128; System.out.println("int == int, result:" + (i1 == i2)); // true System.out.println("----------------------------------------------------");Copy the code

Primitive types use == to compare values, so this is unambiguously true.

Situation (2) :

Int i3 = 128; int i3 = 128; Integer i4 = new Integer(128); Println ("int == new Integer, result:" + (i3 == i4)); system.out.println ("int == new Integer, result:"); // true System.out.println("----------------------------------------------------");Copy the code

The base type is compared to the wrapper type, and the wrapper type is unpacked and then compared, so it’s still true.

Situation (3) :

Int i5 = 128; int i5 = 128; Integer i6 = 128; Println ("int == Integer autoBoxing, result:" + (i5 == i6)); system.out.println ("int == Integer autoBoxing, result:"); // true System.out.println("----------------------------------------------------");Copy the code

I6 (int, 128); i6 (int, 128); i6 (int, 128);

Situation (4) :

Integer i7 = new Integer(128); Integer i8 = new Integer(128); System.out.println("new Integer == new Integer, result:" + (i7 == i8)); // false System.out.println("----------------------------------------------------");Copy the code

Here are two newly generated wrapper class objects to compare, here is the memory address of their respective objects, because two different objects, so the address value must be different, the result is false.

(5) :

Integer i9 = 128; Integer i10 = new Integer(128); System.out.println("Integer autoBoxing == new Integer, result:" + (i9 == i10)); // false System.out.println("----------------------------------------------------");Copy the code

Here i9 automatically boxes 128 into type Integer when assigned, so you end up comparing the memory addresses of two different objects, which must be false.

Situation 6:

Integer i11 = 128; Integer i11 = 128; Integer i11 = 128; Integer i12 = 128; Println ("Integer autoBoxing == Integer autoBoxing, interval [-128, 127], result:" + (i11 == i12)); // false System.out.println("----------------------------------------------------");Copy the code

For i10 and i11, 128 will be boxed into an Integer and then compared, as in case 5, so the final result will be false.

As you may have noticed, the number I’m using here is 128, which is chosen because Integer’s cache pool is not involved in the comparison.

Integer Specifies the cache pool

Here’s an example of an Integer cache pool:

// Automatic boxing will fetch objects from the cache pool, the cache pool range is [-128, 127] // Automatic boxing Integer objects and automatic boxing Integer objects comparison, [-128, 127] Integer i13 = 127; // Cache pool Integer I14 = 127; Println ("Integer autoBoxing == Integer autoBoxing, interval [-128, 127], result:" + (i13 == i14)); // trueCopy the code

This example is exactly the same as the above case (⑥), except that 127 is used in the case (⑥) and 128 is used in the case (⑥), but the result of ⑥ is false and the comparison result here is true, which is confusing.

The assignment of i13 and i14 triggers automatic boxing, generating the corresponding Integer object for 127. We then use == to compare the addresses of the two Integer objects, resulting in a true output.

We know that in Java, if two objects have the same memory address, they are the same object and allocated the same block of memory. Both i13 and I14 are generated by automatic boxing. Is the same object returned in the process of generation?

Those of you who have questions about automatic boxing and unboxing can read this article to help you understand automatic boxing and unboxing in Java.

Integer#valueOf() : Integer#valueOf()

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

As you can see, if the value of I is within a certain range, an Integer object is returned from the integerCache. cache array; If you’re out of that range you just return an Integer object.

Let’s look at the code for 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; . 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

By convention, extraneous code is omitted.

The default low value is -128, and the default high value is 127. The cache is an array of type Integer.

The static block does two things: (1) initialize the Integer cache array (size: 256). ② Assign values to all elements in the cache. In the for loop, each cache element is assigned a value starting at -128 and going up to 127, corresponding to the closed range [-128, 127], which is exactly 256 elements.

So what we call an Integer’s cache pool is actually integerCache. cache, which is initialized when an Integer loads its class.

The int #valueOf method is called when the basic int needs to be automatically boxed. In the implementation of the int #valueOf method, if the valueOf an int is in the range of the integercache. cache (closed range [-128, 127]), Cache returns the Integer object that has been constructed, depending on the script, from the array integerCache. cache.

I13 and I14 get the same Integer object (because they have the same value, the corresponding index in the integerCache. cache array is the same). So we get the same cache object).

Byte, Short, Integer, Long, Character, Boolean Specifies the corresponding cache pool.

Since Integer has a cache pool, does the same apply to other wrapped classes?

Let’s take a look at the test code:

// Compare the Byte objects that are automatically boxed with the Byte objects that are automatically boxed. Byte B1 = 127 in the range [-128, 127]; Byte b2 = 127; Println ("Byte autoBoxing == Byte autoBoxing, interval [-128, 127], result:" + (b1 == b2)); // true System.out.println("----------------------------------------------------"); Short s1 = 127; Short s1 = 127; Short s1 = 127; // cache pool Short s2 = 127; Println ("Short autoBoxing == Short autoBoxing, interval [-128, 127], result:" + (s1 == s2)); // true System.out.println("----------------------------------------------------"); [-128, 127] : Long l1 = 127L; // cache pool Long l2 = 127L; Println ("Long autoBoxing == Long autoBoxing, interval [-128, 127], result:" + (l1 == l2)); // true System.out.println("----------------------------------------------------"); [-128, 127] (c1 = 127); // cache pool Character c2 = 127; Println ("Character autoBoxing == Character autoBoxing, interval [-128, 127], result:" + (c1 == c2)); // true System.out.println("----------------------------------------------------"); Boolean bool1 = true in the range [-128, 127]; // Cache pool Boolean BOOL2 = true; Println ("Boolean autoBoxing == Boolean autoBoxing, range [true, false], result:" + (BOOL1 == BOOL2)); // true System.out.println("----------------------------------------------------");Copy the code

The cache pool for Byte, Short, Integer, Long, and Character is [-128, 127]. The cache pool for Boolean is special. There are only true and false Boolean objects.

The summary is as follows:

Basic types of A wrapper class Buffer pool
byte Byte [- 128, 127]
short Short [- 128, 127]
int Integer [- 128, 127]
long Long [- 128, 127]
char Character [- 128, 127]
boolean Boolean [true, false]
float Float There is no
double Double There is no

reference

This article takes you through automatic boxing and unboxing in Java