Interview Question Series Part 3: The inside story of Integer equal sign judgment, you may not know?

"Java Interview Questions Series": In-depth exploration of the classic content of the interview questions, analysis of the source code, summary principles, forming a series of articles on the official account, interview or not can improve skills. Welcome everyone to continue to pay attention to [Program New Vision]. This is the third in the series.

In the interview process, there are endless questions about Integer comparison "==", but no matter how you change, as long as you understand the underlying principles, you can get the answer immediately, and you don't have to memorize the exam questions by rote.

There is such a mandatory requirement in the "Alibaba Java Development Manual":

"All the comparisons of values ​​between the plastic packaging objects are compared using the equals method. Explanation: For the assignment of Integer var=? in the range of -128 to 127, the Integer object is generated in IntegerCache.cache and will reuse the existing object. The Integer value of this interval can be directly judged with ==, but all data outside this interval will be generated on the heap, and existing objects will not be reused. This is a big pit, and it is recommended to use the equals method for judgment."

In fact, if you memorize the above paragraph, you can basically answer 50% of the interview questions correctly (about the probability of guessing). But if you want to understand the deep principles and the remaining 50% of the questions, let's continue to look down.

Interview questions

First look at a common interview question, compare the above conclusions, and see how many items can be answered correctly. How many items are printed as true in the following code?

@Test
public void test2() {
	Integer i1 = 64;
	int i2 = 64;

	Integer i3 = Integer.valueOf(64);
	Integer i4 = new Integer(64);

	Integer i5 = 256;
	Integer i6 = Integer.valueOf(256);

	System.out.println("A:" + (i1 == i2));
	System.out.println("B:" + (i1 == i3));
	System.out.println("C:" + (i3 == i4));
	System.out.println("D:" + (i2 == i4));
	System.out.println("E:" + (i3.equals(i4)));
	System.out.println("F:" + (i5 == i6));
}

Execute the above program, the print result is:

A:true
B:true
C:false
D:true
E:true
F:false

Only C and F items are printed as false. Are you wondering why i1 is equal to i2, i1 is equal to i3, i2 is equal to i4, all are true, then according to the transitivity of the equal sign, i3 should be equal to i4?

Why are i1 and i3 equal, but i5 and i6 are not equal?

Keep the doubt first. Below, we analyze the storage structure of int and Integer in JVM. Master the underlying storage structure, and you will find that no matter how the subject changes, it will never change.

Variable storage in JVM

Before thoroughly clarifying the above problem, let's first understand the storage of basic type variables and reference type variables in the JVM.

Usually variables are divided into local variables and global (member) variables. Local variables are variables declared in methods; global variables are member variables declared in classes.

The variables and values ​​of the basic types are together when they are allocated, and they are all in the method area or stack memory or heap memory. The variable and value of a reference type are not necessarily together.

Local variables are stored in the method stack

When the method is called, the Java virtual machine creates a stack frame synchronously, and the local variables are stored in it. When the method ends, the virtual machine releases the method stack, and the declared variables end with the destruction of the stack frame. Therefore, local variables can only be valid in methods.

In this process, the storage of basic types and reference types are different:

(1) Basic types: variables and corresponding values ​​are stored in the stack of the JAVA virtual machine;

(2) Reference type: The variable is stored in the stack and is a memory address, and the address value points to an object in the heap.

image

The stack belongs to the thread private space, and the life cycle and scope of local variables are generally very short. In order to improve the efficiency of gc, it is not necessary to put it in the heap.

Global variables are stored in the heap

Global variables are stored in the heap and will not be destroyed when the method ends. Variables declared in the class are also divided into basic types and reference types.

(1) Basic types: variable names and values ​​are stored in heap memory.

(2) Reference type: Variable is a reference address, which points to the referenced object. At this point, variables and objects are in the heap.

For a simple example, the following code:

public class Person {
	int age = 10;
	String name = "Tom";
}

The corresponding storage structure of age and name is as follows:
image

Combining the above theory, we use a piece of code to analyze the storage location of various types.

public class DemoTest {

 int y; // 变量和值均在堆上
 
 public static void main(String[] args) {

     int x = 1; // 变量和值分配在栈上
     
     String name = new String("cat"); // 数据在堆上,name变量的指针在栈上
     
     String address = "北京"; // 数据在常量池,属于堆空间,指针在栈上
     
     Integer price = 4; // 包装类型为引用类型,编译时会自动装拆箱,数据在堆上,指针在栈
 }
}

Basic types of in-stack storage

Through the above examples, we have basically understood the memory allocation of different types of values. Below we focus on local variables.

Let's first look at the processing mode for the int type in the same stack frame.

int a = 3;
int b = 3;

Both a and b in the above code are local variables. Assuming that the compiler processes int a=3 first, it will create a reference variable of a in the stack, and then find out whether the value of 3 exists in the stack, if not, store 3 in, and then point a to 3.

Then deal with int b=3, after creating the reference variable of b, the search is also performed. Because there is already a value of 3 in the stack, b points directly to 3.

At this time, a and b point to the value 3 at the same time, which is naturally equal.

Regarding the low-level comparison between basic types and reference types, you can extend it a bit: For the "==" operation symbol, the JVM will generate different instructions at compile time according to the types of operands that are compared on both sides:

(1) For boolean, byte, short, int, and long integer operands, an if_icmpne instruction is generated. This instruction is used to compare whether the integer values ​​are equal.

(2) If the operand is an object, the compiler will generate an if_acmpne instruction, which changes i(int) to a(object reference) compared with if_icmpne.

Back to the topic

After studying the underlying theoretical knowledge above, we can basically draw the following conclusions: (1) To compare two int types, use the double equal sign directly; (2) When comparing the Integer object of the int package class, use equals to compare OK.

But the above results can only say that the E project is correct. The comparison item also involves the packing and unpacking operations of shaping, and the cache of Integer. Let us analyze one by one below.

Comparison of different creation forms

First look at the initialization of Integer. According to the internal implementation of Integer, there are three types of Integer creation, namely:

Integer a = new Integer(1); //创建新的类

Integer b = Integer.valueOf(2);  

Integer c = 3; //自动包装,会调用valueOf方法

The bottom layer of direct assignment will call the valueOf method to operate, so the effect of these two operations is the same.

Because the two objects created by new and valueOf are completely two objects, for the C item in the question, directly comparing the references of the two objects is definitely not equal, so the result is false. But why is item B true? We will talk about it later.

Unboxing in comparison

In the question, we found that both A and D are true, and their comparison format is the comparison between the basic type and the packaging type.

For this type of comparison, the packaging type will be automatically unboxed and become the basic type (int). Obviously, the results are equal.

Integer cache

Why are i1 and i3 equal, but i5 and i6 are not equal? Correspond to items B and G in the question. This involves the caching mechanism of Integer.

As we already know above, Integer direct assignment and valueOf are equivalent, let's take a look at valueOf and related methods first.

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

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() {}
    }

The valueOf method determines whether the number is greater than low (-128) and less than high (127). If the condition is met, the corresponding number is directly returned from the IntegerCache.

IntegerCache is used to store some commonly used numbers to prevent repeated creation. It is initialized by static code when the Integer class is loaded into memory.

So as long as the object is created by direct assignment of valueOf or Integer, and its value is less than 127 and greater than -128, it is true whether it is compared with == or equals.

The above source code and principles also explain the reasons explained in the Ali Java Development Manual.

Why equals can circumvent the problem

For numbers that do not meet the range of -128 to 127, no matter how they are created, a new object will be created and can only be compared by equals. Next we will look at the equals method.

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

The implementation of equals is relatively simple, first compare whether the types are consistent, if they are inconsistent, return false directly; otherwise, compare the two values, and return true if they are the same.

summary

The core points of Integer's comparison include the following three points: the storage structure of the referenced object, the caching mechanism of Integer, and automatic boxing and unboxing.

Integer in the == operation, summarize:

(1) If one of the two ends of == is a basic type (int), an automatic unboxing operation will occur, and the value is compared.

(2) If both ends of == are the packaging type (Integer), it will not be automatically unboxed. First, it will face the cache problem. Even the data in the cache will face the problem of creation method again, so it is strongly recommended to use equals Methods to compare.

If you think the article is well written, just pay attention. In the next article, we will talk about rewriting the underlying logic of equals and hashcode methods.

Original link: " Interview Question Series Part 3: The Inside Story of Integer Equal Sign Judgment, Maybe You Don’t Know?


New Vision of Procedure

The public account " New Vision of Program ", a platform that allows you to simultaneously improve your soft power and hard technology, providing massive amounts of data

WeChat Official Account: New Vision of Program

Guess you like

Origin blog.csdn.net/wo541075754/article/details/108240367