Java Advancement--Understanding the toString method of Integer classes from source code

Keywords: JDK Java Programming less

The jdk in Java is a treasure house for learning Java programming. The implementation of various methods not only provides insight into the application of design patterns, but also shows how jdk writers optimize their code, which has reached an extreme level, and is very important for us to write our own code.read the fucking source code is a must to become a great programmatic ape.This blog analyzes the toString method of the Integer class to see how jdk writers write their code.

Basic usage of toString for Integer class

public class IntegerDemo {
    public static void main(String[] args) {

        Integer OUT_MAX_VALUE=new Integer(Integer.MAX_VALUE+1);     

        Integer MAX_VALUE=new Integer(Integer.MAX_VALUE);

        Integer MIN_VALUE=new Integer(Integer.MIN_VALUE);

        Integer NOR_VALUE=new Integer(-128);

        Integer OUT_MIN_VALUE=new Integer(Integer.MIN_VALUE-1);

        System.out.println("max_val :"+MAX_VALUE.toString());
        System.out.println("max_val :"+MAX_VALUE);
        System.out.println("-------------------------");
        System.out.println("out_max :"+OUT_MAX_VALUE.toString());
        System.out.println("out_min :"+OUT_MIN_VALUE.toString());
        System.out.println("-------------------------");        
        System.out.println("min_val :"+MIN_VALUE.toString());
        System.out.println("min_val :"+MIN_VALUE);
        System.out.println("-------------------------");
        System.out.println("nor_val :"+NOR_VALUE.toString());
        System.out.println("nor_val :"+NOR_VALUE);
    }
}
//output
/*
max_val :2147483647
max_val :2147483647
-------------------------
out_max :-2147483648
out_min :2147483647
-------------------------
min_val :-2147483648
min_val :-2147483648
-------------------------
nor_val :-128
nor_val :-128
*/

As you can see from the above, the value of directly outputting Integer is the same as calling the toString method of the Integer class. In fact, when an object is printed directly, the toString method of the object is called.When the toString method is called, it actually outputs the value of Integer, and the toString method converts the value of int type to string type output.Note a few points:

(1) Integer's value ranges from Integer.MIN_VALUE to Integer.MAX_VALUE, and if it goes beyond this range, you will get some strange results.

(2) The result of adding 1 output to Integer.MAX_VALUE is a negative value.

(3) The result of adding 1 to Integer.MIN_VALUE is a positive value.

Source Code Analysis for toString of Integer Class

Here's a closer look at how the toString method of the Integer class is implemented.Or go directly to the source code.

 public String toString() {
        return toString(value);
    }

//------------------------------

 public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
         //Return its string directly if it is the minimum because Integer.MIN_VALUE=-2147483648, which saves the following calculation time
         //①
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
        //Gets the length of an integer value in decimal
        char[] buf = new char[size];
        //②
        getChars(i, size, buf);
        //Get every character in an integer
        //③
        return new String(buf, true);
        //Return string value
    }

The code above does a few things:
(1) Save time if Integer has exactly the value of Integer.MIN_VALUE returning directly to'-2147483648'.
(2) Get the decimal length of the integer value, if the negative number first finds the length of the absolute value, and then adds the length by 1, because the sign bits of the negative number occupy one place.
3. Get each character of the integer value.
(4) Return the new string of the resulting character.
Here's how the stringSize method works:

 static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
                //See how many digits x is
    }
//----------------------------------
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };

The stringSize above is very clever about the length of integers. First, it defines an array in which the maximum of one decimal digit and the maximum of two decimal digits are stored, in turn, until Integer.MAX_VALUE, because the maximum of integer is Integer.MAX_VALUE, that is, the maximum length of integer is 10 digits.Be careful:
(1) The parameter x in stringSize(int x) must be a positive integer.
Here's an example of the stringSize(int x) method.

public class IntegerDemo {
    public static void main(String[] args) {

        int x=199;

        int max=Integer.MAX_VALUE;

        int out_max=Integer.MAX_VALUE+1;

        System.out.println(stringSize(x));
        System.out.println("-------------------------");
        System.out.println(stringSize(max));
        System.out.println("-------------------------");
        System.out.println(stringSize(out_max));        
    }
     final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
         99999999, 999999999, Integer.MAX_VALUE };

     static int stringSize(int x) {
         for (int i=0; ; i++)
             if (x <= sizeTable[i])
                 return i+1;
     }
}
//output
/*
3
-------------------------
10
-------------------------
1
*/

The 199 above is less than 999 so the number of digits is 3, and the value of out_max is negative because it exceeds Integer.MAX_VALUE, resulting in a length of 1. Negative digits must be converted to positive digits when determining their length, and the length of the digits must be added with a bit to place'-'.After getting the length of the integer value, use the getChars method to get the characters on each value.Let's look at this getChars method.

static void getChars(int i, int index, char[] buf) {
        int q, r;
        int charPos = index;
        char sign = 0;

        if (i < 0) {
            sign = '-';
            i = -i;
        }

        // Get two char values each time I >= 65536.
        while (i >= 65536) {
            q = i / 100;
        // really: r = i - (q * 100);
            r = i - ((q << 6) + (q << 5) + (q << 2));
            //Quickly calculate q*100 using shift operation, 2^6+2^5+2^2=64+32+4=100.
            i = q;
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }

        // Get only one char value at a time when I <= 65536
        // assert(i <= 65536, i);
        for (;;) {
            q = (i * 52429) >>> (16+3);
            //q/10,2^19=524288, (double)52429/(1<<19)=0.10000038146972656
            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
            buf [--charPos] = digits [r];
            i = q;
            if (i == 0) break;
        }
        if (sign != 0) {
            buf [--charPos] = sign;
            //If negative plus sign bits
        }
    }
//--------------------------------------
final static char [] DigitOnes = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        } ;

//--------------------------------------
final static char [] DigitTens = {
        '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
        '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
        '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
        '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
        '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
        '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
        '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
        '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
        '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
        '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
        } ;

The getChars method is also clever
1) When I >= 65536, it is possible to find two-bit char values at a time. When evaluating two-bit char values, DigitOnes and DigitTens are used to find two-bit char values at a time with the help of these two arrays. Here's a brief description of how to use these two arrays to find the values of one bit and ten bits, respectively. If you now get 65 for two digits, 5 for each bitTo get 5 on a bit, no matter how many bits there are in the ten, it must be 5, so the array DigitOnes has 5 at 05, 15, 25, 35, 45, 55, 65, 75, 85, 95, so that you can get 5 on either 25 or 35 bits.Let's see how to get the number on the ten, or 65, or 6, so DigitTens has 60, 61, 62, 63, 64,...69 are all 6.

(2) q = (i * 52429) > > (16+3); this code is actually q=i/10, where (double) 52429/(1< 19) = 0.10000038146972656 is one-tenth of the precision of calculating a number in int type, you can see that jdk developers are very aware of this optimization.
(q << 6) + (q << 5) + (q << 2) is equivalent to q*100 because q*2^6+q*2^5+q*2^2=q*(64+32+4)=100q. Why not use q*100 directly here, but use displacement because it is more efficient to use displacement directly at the bottom of q*100, which also gives us some inspiration for programming.When you can optimize, you must optimize.

Posted by mikeabe on Sun, 30 Jun 2019 11:12:18 -0700