Integer源码详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/abcdef314159/article/details/70176707

尊重原创,转载请标明出处    http://blog.csdn.net/abcdef314159

对于Integer这个类估计大家也都非常熟悉了,以前看过他的源码,但也只是粗略的看了一下,最近有时间认真的看了一下发现这个类设计的非常好,所以就打算记录下来与大家共享。我们看一下java项目中的Integer类大概有500多行,并且注释也很少,

而Android中的Integer大概有1000多行,当然他的注释也比较多

既然是要分析,那么就索性两个一起来分析,这里我以Android中的Integer为主,是挑着来分析的,不是按顺序的,先看一下bitCount(int i)这个方法

    /**
     * Returns the number of one-bits in the two's complement binary
     * representation of the specified {@code int} value.  This function is
     * sometimes referred to as the <i>population count</i>.
     *
     * @return the number of one-bits in the two's complement binary
     *     representation of the specified {@code int} value.
     * @since 1.5
     */
    public static int bitCount(int i) {
        // HD, Figure 5-2
        i = i - ((i >>> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
    }

他表示的是计算int类型转化为二进制之后1的个数,举个例子,5用二进制表示为101,那么就返回2,表示有2个1,8用二进制表示是1000,就会返回1,表示有一个1.但是这个方法用的非常妙,直接看可能不是太明白,如果把上面的数字转化成二进制可能就会明白很多,0x55555555用二进制表示就是……01010101(1个0,1个1循环,总共是32位),0x33333333用二进制表示就是……00110011(2个0,2个1循环,总共32位),0x0f0f0f0f用二进制表示就是……0000111100001111(4个0,4个1循环,总共32位),看到这里可能就会稍微有点明白,其实他的原理就是每两位为一个小的单元,计算出1的个数储存在两位数中,然后再以4位为一个小的单元计算前面储存的数的和,储存到4位数中,然后再以8位,16位,以此类推。但这里要注意第一行和后面的几行原理是不同的,第一行是计算1的个数,后面的几行都是对第一行计算的结果相加,举个例子,比如第一行开始计算的时候如果最后两位二进制数为10,那么他表示最后两位只有一个1,因为还一个为0,如果是后面的几行,那么开始计算的时候如果二进制为10,那么他表示最后两位数有2个1,因为从第二行开始每个位上的1和0不在是表示1和0了,他表示的是第一行1和0的个数的和,而二进制10就是十进制2的意思,所以他表示的是2个1。其实第一行很好理解,i往右移动一个单位,再与……01010101(32位)进行与运算,就表示把原来的偶数位变为奇数位,然后再把新的偶数位置为0,再用i减去他,他表示的是把一个数每两个分为一组,然后让每组中的数减去这个组的偶数位上的数字,所以他最终表示的结果就是每两位1的个数储存在原来的两位数中,我画个图就好理解了

我还是写在代码里逐行分析可能会更好一些,下面是代码的分析

    public static int bitCount(int i) {
        // HD, Figure 5-2
        /**
         * 每两位为一个单元,把原来单元中1的个数储存在原来的单元中
         */
        i = i - ((i >>> 1) & 0x55555555);
        /**
         *0x33333333其实就是二进制……00110011(共32位),因为上面的每两位代表1的个数,所以下面的这几行就是要把上面每两位
         * 的数字加起来,下面的这行代码可以这样理解,每4位分为一组,然后4位中的每两位相加,相加的结果在储存到这4位二进制数中,
         * i & 0x33333333表示每4位中的低2位,(i >>> 2) & 0x33333333表示每4位中的高2位,然后在相加
         */
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        /**
         * 这个更好理解,i >>> 4表示往右移动了4位,然后在与i相加,相当于每8位一组,然后8位中的高4位与低4位相加储存在低4位中,
         * 然后这里在与0x0f0f0f0f进行与运算,把高4位完全置为0了,因为0x0f0f0f0f用二进制表示就是00001111000011110000111100001111,
         * 看到这里可能有些困惑,这里为什么要与0x0f0f0f0f进行与运算,因为每8位一组的话,最多也就是8,那么4位数足够了,高4位就没有必要了,
         * 如果不置为0有没有影响,其实如果1的位数极少的话是没什么影响的,但如果1的位数比较多到后面计算的结果可能就会往前进位,导致结果错误,
         * 所以这一步要进行一次与运算,那为什么上面的那行代码没有把4位一组中的高两位置0,这是因为4位一组最多有4个1,而2位二进制数最多表示3,
         * 小于4,所以不能置为0,
         *
         */
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        /**
         * 和上面类似,每16位分为一组,每组中的高8位和低8位相加,这里的代码相加的很干净,因为无论是高8位还是低8位中的前4位在上面一行中
         * 都已经置为0了,这里也可以像上面那样,加完之后在与0x00ff00ff进行与运算,但其实这里已经没有必要了,因为int类型为32位,
         * 最多也就32个1,用8位数储存足够了,所以不会超过8位,也就不用担心超过8位在往前进1位的问题了。
         */
        i = i + (i >>> 8);
        /**
         * 和上面类似,就不在详述
         */
        i = i + (i >>> 16);
        /**
         * 到最后为什么要和0x3f进行与运算,0x3f用二进制表示就是111111,因为上面两行没有进行与运算,所以前面的数据都是无效的,
         * 只有最后8位是有效的,而后8位的前两位不用说肯定为0,因为最多也就32个1,用后面6位数表示就已经足够了,所以这里与0x3f
         * 进行与运算,来计算出最终1的个数
         */
        return i & 0x3f;
    }

再来看一下java中的bitCount(int var0)的方法

    public static int bitCount(int var0) {
        var0 -= var0 >>> 1 & 1431655765;
        var0 = (var0 & 858993459) + (var0 >>> 2 & 858993459);
        var0 = var0 + (var0 >>> 4) & 252645135;
        var0 += var0 >>> 8;
        var0 += var0 >>> 16;
        return var0 & 63;
    }

其实他和Android中的bitCount是一样的,只不过他是10进制,而Android中的是16进制。我们再来看Android中的下一个方法

    /**
     * Returns an {@code int} value with at most a single one-bit, in the
     * position of the highest-order ("leftmost") one-bit in the specified
     * {@code int} value.  Returns zero if the specified value has no
     * one-bits in its two's complement binary representation, that is, if it
     * is equal to zero.
     *
     * @return an {@code int} value with a single one-bit, in the position
     *     of the highest-order one-bit in the specified value, or zero if
     *     the specified value is itself equal to zero.
     * @since 1.5
     */
    public static int highestOneBit(int i) {
        // HD, Figure 3-1
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }

他表示的就是从左往右数,遇到第一个1保留,则其之后的所有全部清零,我可以这样来理解,就是小于或等于这个数的最大的2的n次方,当i等于2的n次方的时候,结果还是等于i,当i不是2的n次方的时候就会返回小于i的最大的2的n次方,举个例子,如果i是8则返回8,因为8是2的3次方,如果i是31,则返回16,因为2的4次方是16小于31,而2的5次方是32,大于31,所以32不合适。说到这里,自然会联想到之前写的Android HashMap源码详解中讲到的roundUpToPowerOfTwo(int i)方法,而roundUpToPowerOfTwo方法返回的是大于或等于i的最小的2的n次方。highestOneBit的原理其实很简单,他就是通过左边遇到第一个1然后不停的右移然后在进行与运算,把1后面全部置为1,最后一步在通过i - (i >>> 1)把左边第一个1之后的全部置为0,这个可以参照一下roundUpToPowerOfTwo的原理图,大家可以这样理解,当i是2的n次方的时候highestOneBit函数和roundUpToPowerOfTwo函数返回的结果是一样的,当i不是2的n次方的时候,roundUpToPowerOfTwo返回的结果是highestOneBit的2倍。其实讲到这就已经基本上结束了,但由于好奇又看了一下HashMap中初始化数组大小的方法,发现又变了,不得不感慨Android的源码变化实在是太快了,这次看的是Android-25的,代码如下

    private static int roundUpToPowerOf2(int number) {
        // assert number >= 0 : "number must be non-negative";
        int rounded = number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (rounded = Integer.highestOneBit(number)) != 0
                    ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded
                    : 1;

        return rounded;
    }

我们看到他已经换成Integer的highestOneBit方法了,我们看到有这样一段代码(Integer.bitCount(number) > 1),他表示number是不是2的n次方,如果是就返回rounded,如果不是就返回rounded<<1,因为我们知道highestOneBit是返回小于或等于number的最大的2的n次方,而HashMap初始化的大小是不能小于number的,所以当number为2的n次方的时候直接返回,当number不为2的n次方的时候就会返回rounded的2倍。而java中的highestOneBit和Android中的highestOneBit方法基本类似,这里就不在贴出,下面再来看下一个方法,

    /**
     * Returns the value obtained by reversing the order of the bits in the
     * two's complement binary representation of the specified {@code int}
     * value.
     *
     * @return the value obtained by reversing order of the bits in the
     *     specified {@code int} value.
     * @since 1.5
     */
    public static int reverse(int i) {
        // HD, Figure 7-1
        i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
        i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
        i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
        i = (i << 24) | ((i & 0xff00) << 8) |
            ((i >>> 8) & 0xff00) | (i >>> 24);
        return i;
    }

他表示把i转化为二进制数,然后再把这个二进制数反转,只要把上面的16进制数转化为二进制就一目了然了,我们先来看一下字母的反转,再来分析上面的就容易多了

看完这个图之后,那么上面的代码就非常容易理解了,先看第一行(i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;先把0x55555555转化为二进制,会发现(i & 0x55555555) << 1表示把i的偶数为置0,奇数位不变,然后在左移,把原来的奇数位变成了现在的偶数位,而原来的偶数位变为现在的奇数位,然后全部置为0了,(i >>> 1) & 0x55555555右移一位,把原来的偶数为变为奇数位了,然后在与0x55555555进行与运算,相当于把原来的奇数位放到现在的偶数位上,然后全部置为0,原来的偶数位不变然后放到现在的奇数位上,最后来一个|(或)运算,就相当于把原来的奇偶位交换了。而(i & 0x33333333) << 2 | (i >>> 2) & 0x33333333表示每4个一组,然后每组的前后两个进行交换,同理(i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f表示每8个一组,然后每组的前4个和后4个进行交换。关键来看最后一行(i << 24) | ((i & 0xff00) << 8) |((i >>> 8) & 0xff00) | (i >>> 24),因为前面已经交换到每8个一组了,所以到这里也是每8个分为一组,下面还是画个图来理解一下吧

OK,下面再来看下一个方法

    /**
     * Returns the value obtained by reversing the order of the bytes in the
     * two's complement representation of the specified {@code int} value.
     *
     * @return the value obtained by reversing the bytes in the specified
     *     {@code int} value.
     * @since 1.5
     */
    public static int reverseBytes(int i) {
        return ((i >>> 24)           ) |
               ((i >>   8) &   0xFF00) |
               ((i <<   8) & 0xFF0000) |
               ((i << 24));
    }

这个和上面分析的i = (i << 24) | ((i & 0xff00) << 8) |((i >>> 8) & 0xff00) | (i >>> 24);其实是一样的,他表示反转字节,因为每个字节占8位,所以每次移动8位。接着往下看

    /**
     * Returns the number of zero bits following the lowest-order ("rightmost")
     * one-bit in the two's complement binary representation of the specified
     * {@code int} value.  Returns 32 if the specified value has no
     * one-bits in its two's complement representation, in other words if it is
     * equal to zero.
     *
     * @return the number of zero bits following the lowest-order ("rightmost")
     *     one-bit in the two's complement binary representation of the
     *     specified {@code int} value, or 32 if the value is equal
     *     to zero.
     * @since 1.5
     */
    public static int numberOfTrailingZeros(int i) {
        // HD, Figure 5-14
        int y;
        if (i == 0) return 32;
        int n = 31;
        y = i <<16; if (y != 0) { n = n -16; i = y; }
        y = i << 8; if (y != 0) { n = n - 8; i = y; }
        y = i << 4; if (y != 0) { n = n - 4; i = y; }
        y = i << 2; if (y != 0) { n = n - 2; i = y; }
        return n - ((i << 1) >>> 31);
    }

他表示返回从右边数第一个1右边0的个数,如果i是12,则返回2,因为12的二进制是1100,右边有2个0,如果i是0,则返回32,还是在代码里分析效果要好一些

    public static int numberOfTrailingZeros(int i) {
        // HD, Figure 5-14
        int y;
        //等于0的时候返回32
        if (i == 0) return 32;
        //因为返回的是右边第一个1的右边的0的个数,如果不是0,则0的个数肯定不会大于31
        int n = 31;
        /**
         *  左移16位,如果y==0,说明原来i的低16位上是没有1的,只能说明高16位上有1,那么n肯定是大于或等于16。
         *  如果y!=0,说明原来i的低16位上肯定是有1,那么n就会小于16,只需要运算低16就行了,所以就把y赋值给i,
         *  同时n也要减去16,
         */
        y = i <<16; if (y != 0) { n = n -16; i = y; }
        //同上
        y = i << 8; if (y != 0) { n = n - 8; i = y; }
        //同上
        y = i << 4; if (y != 0) { n = n - 4; i = y; }
        //同上
        y = i << 2; if (y != 0) { n = n - 2; i = y; }
        /**
         * (i << 1) >>> 31表示计算i的第31位上的值,i << 1表示把i的第31位变成32位,然后再无符号的右移31位,
         所以得到的结果就是i的第31位上的值,但是要记住这个i不是最初的那个i,而是运算之后的i,因为上面有赋值,
         当满足条件的时候i就会改变。这里为什么要减去第31位上的值,这是因为上面最后一行运行之前其实i已经
         判断了(16+8+4=28)位了,当上面最后一行运行之后就只剩下最后两位了,我们知道通过上面一步步的运算,
         到这里i的第32位和31位至少有1个1,不可能全是0,如果全是0只有当最初始的i为0的时候,那么这是不可能的,
         因为如果最初始的i为0,在最上面就已经被拦截了,直接返回32了。这里为什么要减去31位上的值而不是32位上的值,
         这就是算法的巧妙之处,因为这个方法返回的是右边第一个1右边0的个数,所以这里只需要判断第31位就行了,因为如果
         31位为1的话,那么32位就没他什么事了,不管他是0还是1都不会有影响,因为31位是在32位的右边(最右边是第一位,
         最左边是第32位),只需要把31位的1减掉就行了,如果31位是0,那么32位肯定为1,因为31和32位必须有一个是1,
         ,其实他这里是把代码简化了,还可以这样写,把最后一行注释掉,改为下面这样,运算结果是一样的
         // y = i << 1; if (y != 0) { n = n - 1; }
         // return n ;
         */
        return n - ((i << 1) >>> 31);
    }

还有一个和他类似的方法,我们可以看一下

    /**
     * Returns the number of zero bits preceding the highest-order
     * ("leftmost") one-bit in the two's complement binary representation
     * of the specified {@code int} value.  Returns 32 if the
     * specified value has no one-bits in its two's complement representation,
     * in other words if it is equal to zero.
     *
     * <p>Note that this method is closely related to the logarithm base 2.
     * For all positive {@code int} values x:
     * <ul>
     * <li>floor(log<sub>2</sub>(x)) = {@code 31 - numberOfLeadingZeros(x)}
     * <li>ceil(log<sub>2</sub>(x)) = {@code 32 - numberOfLeadingZeros(x - 1)}
     * </ul>
     *
     * @return the number of zero bits preceding the highest-order
     *     ("leftmost") one-bit in the two's complement binary representation
     *     of the specified {@code int} value, or 32 if the value
     *     is equal to zero.
     * @since 1.5
     */
    public static int numberOfLeadingZeros(int i) {
        // HD, Figure 5-6
        if (i == 0)
            return 32;
        int n = 1;
        if (i >>> 16 == 0) { n += 16; i <<= 16; }
        if (i >>> 24 == 0) { n +=  8; i <<=  8; }
        if (i >>> 28 == 0) { n +=  4; i <<=  4; }
        if (i >>> 30 == 0) { n +=  2; i <<=  2; }
        n -= i >>> 31;
        return n;
    }

这个是返回左边开始连续的为0的个数,这个可以参照上面一个,就不在详细分析,我们可以看到最后一行减去的是运算到最后i的第32位,和上面一个正好相反,因为这个方法返回的是左边开始0的个数。下面再看另一个方法

    /**
     * Returns the signum function of the specified {@code int} value.  (The
     * return value is -1 if the specified value is negative; 0 if the
     * specified value is zero; and 1 if the specified value is positive.)
     *
     * @return the signum function of the specified {@code int} value.
     * @since 1.5
     */
    public static int signum(int i) {
        // HD, Section 2-7
        return (i >> 31) | (-i >>> 31);
    }

这个很简单吧,看名字就知道什么意思,判断符号,正数返回1,负数返回-1,0返回0。如果i是正数,那么i >> 31肯定为0,-i为负数,-i >>> 31无符号右移,肯定为1,所以结果为1。当i为0的时候结果为0,这个就不在分析。当i为负数的时候,i >> 31结果为……1111(共32个1),-i >>> 31无论结果是什么,最终结果都是……1111(32个1),他是-1的补码,所以结果为-1。接着往下看

    /**
     * Returns the value obtained by rotating the two's complement binary
     * representation of the specified {@code int} value right by the
     * specified number of bits.  (Bits shifted out of the right hand, or
     * low-order, side reenter on the left, or high-order.)
     *
     * <p>Note that right rotation with a negative distance is equivalent to
     * left rotation: {@code rotateRight(val, -distance) == rotateLeft(val,
     * distance)}.  Note also that rotation by any multiple of 32 is a
     * no-op, so all but the last five bits of the rotation distance can be
     * ignored, even if the distance is negative: {@code rotateRight(val,
     * distance) == rotateRight(val, distance & 0x1F)}.
     *
     * @return the value obtained by rotating the two's complement binary
     *     representation of the specified {@code int} value right by the
     *     specified number of bits.
     * @since 1.5
     */
    public static int rotateRight(int i, int distance) {
        return (i >>> distance) | (i << -distance);
    }

他表示循环右移指定位数,移除的不是舍去,而是补到左边,举个例子

        for (int i = 0; i < 100; i++) {
            System.out.println(buwei(Integer.toBinaryString(i)) + "===" + buwei(Integer.toBinaryString(Integer
                    .rotateRight(i, 4))));
        }

看一下打印结果

只截取了前面的几个,他表示往右移动4位,移除的填充到左边。代码很好理解,如果distance为正数,i << -distance表示往左边移动(32-distance)位,i >>> distance表示往右边无符号移动distance位,还有另一个类似的方法

    public static int rotateLeft(int i, int distance) {
        return (i << distance) | (i >>> -distance);
    }

这个和上面一个相反,就不在详述,接着往下看

    /**
     * Returns an {@code int} value with at most a single one-bit, in the
     * position of the lowest-order ("rightmost") one-bit in the specified
     * {@code int} value.  Returns zero if the specified value has no
     * one-bits in its two's complement binary representation, that is, if it
     * is equal to zero.
     *
     * @return an {@code int} value with a single one-bit, in the position
     *     of the lowest-order one-bit in the specified value, or zero if
     *     the specified value is itself equal to zero.
     * @since 1.5
     */
    public static int lowestOneBit(int i) {
        // HD, Section 2-1
        return i & -i;
    }

这个返回的是从右边起遇到第一个1保留,其他的全部置为0,他和highestOneBit正好相反,highestOneBit表示从左边起遇到第一个1,其他位全部置0。代码很简洁。关于算法的基本上已经分析完了,下面来看一个重量级的类

    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    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) {
                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);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

他里面有个cache数组,缓存了-128到127共256Integer对象,每次创建Integer对象的时候,如果值为-128到127,就会从缓存中取,否则就会重新new一个,看一段代码

        Integer i1=120;
        Integer i2=120;
        Integer i3=130;
        Integer i4=130;
        System.out.println(i1==i2);
        System.out.println(i3==i4);

再来看一下运行结果

结果完全意料之中,因为i1和i2为120,小于127,从缓存中取,是同一对象,而i3和i4大于127,所以创建的是两个不同的对象。接着往下看

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

从注释中也可以看到如果i的范围是-128到127,那么就从缓存中取,否则就new一个。接着decode(String nm)代码很简单就不在分析。另外有两个常用的方法toBinaryString和toHexString分别表示转化为二进制字符串和16进制字符串,其实调用的都是同一个方法toUnsignedString(int i, int shift),他表示将Integer转化为无符号字符串,来看一下

    private static String toUnsignedString(int i, int shift) {
         //int类型总共32位
        char[] buf = new char[32];
        int charPos = 32;
        //radix基数,如果是二进制radix为2,如果是16进制radix就为16,一般为2的n次方
        int radix = 1 << shift;
        //这个在讲到HashMap的时候说过,2的n次方减去1则后面全为1,原来为1的位置及之前全为0.
        int mask = radix - 1;
        do {
             //存到数组中
            buf[--charPos] = digits[i & mask];
            //偏移量,这里很好理解,因为Integer是32位,如果shift为3就是八进制,为4就是16进制,这里偏移就是按照多少进制来偏移的。
            i >>>= shift;
        } while (i != 0);

        return new String(buf, charPos, (32 - charPos));
    }

代码很简单,就不在详细介绍。记得以前经常会有这样的烦恼,就是如果我打印二进制的时候我要求必须打印32位,因为这样好进行比较,但实际情况不是这样,但可以把上面代码改一下达到要求

    private static String toUnsignedString(int i, int shift) {
        //int类型总共32位
        char[] buf = new char[32];
        Arrays.fill(buf, '0');//增加
        int charPos = 32;
        //radix基数,如果是二进制radix为2,如果是16进制radix就为16,一般为2的n次方
        int radix = 1 << shift;
        //这个在讲到HashMap的时候说过,2的n次方减去1则后面全为1,原来为1的位置及之前全为0.
        int mask = radix - 1;
        do {
            //存到数组中
            buf[--charPos] = digits[i & mask];
            //偏移量
            i >>>= shift;
        } while (i != 0);

        //return new String(buf, charPos, (32 - charPos));
        return new String(buf, 0, 32);//修改
    }

我们看一下

        for (int i = -100; i <100 ; i++) {
            System.out.println(toUnsignedString(i,1));
        }

再看一下运行结果,我从中间截取一部分

11111111111111111111111111110111
11111111111111111111111111111000
11111111111111111111111111111001
11111111111111111111111111111010
11111111111111111111111111111011
11111111111111111111111111111100
11111111111111111111111111111101
11111111111111111111111111111110
11111111111111111111111111111111
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010
00000000000000000000000000000011
00000000000000000000000000000100
00000000000000000000000000000101
00000000000000000000000000000110

我们看到每个都是32位。

    final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };

    // Requires positive x
    static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
    }

stringSize(int x)表示的x是几位数。0到9是一位数,10到99是两位数……。接下来看一下getChars(int i, int index, char[] buf)函数,他表示把i提取到buf中,index是buf的长度,通过DigitTens和DigitOnes可以看到i是转化为10进制的。这个函数比较复杂,打算在代码中分析

    /**
     * Places characters representing the integer i into the
     * character array buf. The characters are placed into
     * the buffer backwards starting with the least significant
     * digit at the specified index (exclusive), and working
     * backwards from there.
     *
     * Will fail if i == Integer.MIN_VALUE
     */
     //从注释中可以看出,如果i==Integer.MIN_VALUE,这个方法将失效。
    static void getChars(int i, int index, char[] buf) {
        int q, r;
        int charPos = index;
        char sign = 0;//表示符号位

        if (i < 0) {
            sign = '-';
            i = -i;//如果i为负数,变为正数。
        }

        // Generate two digits per iteration
        while (i >= 65536) {//当大于65536的时候每两位开始操作
            q = i / 100;
        // really: r = i - (q * 100);
	    //r是i对100求余的结果,相当于r=i%100;为什么不这样写,我个人认为应该是效率问题,通过移位操作效率要高一些,
	    //((q << 6) + (q << 5) + (q << 2))相当于q*64+q*32+q*4也就是q*100;
            r = i - ((q << 6) + (q << 5) + (q << 2));
            i = q;
	    //把数字存储到数组中,注意这个数组是从后往前存放的,如果仔细查看DigitTens和DigitOnes,可以发现DigitOnes[r]其实相当于DigitOnes[r%10],
	    //DigitTens[r]其实相当于DigitTens[r/10]
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }

        // Fall thru to fast mode for smaller numbers
        // assert(i <= 65536, i);
        for (;;) {
	    //这里2^19=524288,(i * 52429) >>> (16+3)等于52429/524288=0.1000003814697266,相当于i除以10.因为乘法和移位的效率要高于除法,
	    //至于上面的为什么没有使用乘法和移位,是因为当i大于65536的时候在乘法容易溢出,这里为什么要选择19,待会在下面再看
            q = (i * 52429) >>> (16+3);
	    //求余,相当于r=i-q*10;上面的q约等于i/10;
            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
            buf [--charPos] = digits [r];//存放到数组中。
            i = q;
            if (i == 0) break;
        }
        if (sign != 0) {//如果i为负数,把负号添加到buf的前面,切记buf是从后往前添加的
            buf [--charPos] = sign;
        }
    }

下面来看一下上面为什么选择19的问题,我写了一段代码

	private static void calculate() {
		// 相乘的数不能比max大,否则当i接近65536的时候会出现Integer溢出,
		int max = (int) (Integer.MAX_VALUE * 1.0 / (1 << 16) * 10);// 1<<16相当于65536;
		int j = 0;// 移动j位接近max
		int t = 1;// 临时变量,主要用于计算j的值。
		while (t < max) {
			t = (t << 1);
			j++;
		}
		// 最大移动位j,当1移动j位时相当于调用 highestOneBit(int i)函数。
		for (int m = 1; m <= j; m++) {
			int k = 1 << m;
			int mole = k % 10;
			int q = k / 10;
			if (mole >= 5)// 四舍五入
				q++;
			System.out.println(m + "→→→→" + q * 1.0d / k);
		}
	}

上面都有注释,很简单就不在介绍,下面看一下运行结果

1
0
1→→→→0.0
2→→→→0.0
3→→→→0.125
4→→→→0.125
5→→→→0.09375
6→→→→0.09375
7→→→→0.1015625
8→→→→0.1015625
9→→→→0.099609375
10→→→→0.099609375
11→→→→0.10009765625
12→→→→0.10009765625
13→→→→0.0999755859375
14→→→→0.0999755859375
15→→→→0.100006103515625
16→→→→0.100006103515625
17→→→→0.09999847412109375
18→→→→0.09999847412109375
19→→→→0.10000038146972656

我们看到当i越大的时候,结果越接近于0.1.所以选择i等于19.下面再看另一个方法toString(int i),代码也很简单,就简单介绍一下

    /**
     * Returns a {@code String} object representing the
     * specified integer. The argument is converted to signed decimal
     * representation and returned as a string, exactly as if the
     * argument and radix 10 were given as arguments to the {@link
     * #toString(int, int)} method.
     *
     * @param   i   an integer to be converted.
     * @return  a string representation of the argument in base 10.
     */
    public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";

        // Android-changed: cache the string literal for small values.
        boolean negative = i < 0;//判断是否是负数
        boolean small = negative ? i > -100 : i < 100;
        if (small) {//如果i大于等于-100并且小于等于100,则从缓存中取
            final String[] smallValues = negative ? SMALL_NEG_VALUES : SMALL_NONNEG_VALUES;

            if (negative) {
                i = -i;
                if (smallValues[i] == null) {//如果缓存为空则创建
                    smallValues[i] =
                        i < 10 ? new String(new char[]{'-', DigitOnes[i]})
                               : new String(new char[]{'-', DigitTens[i], DigitOnes[i]});
                }
            } else {
                if (smallValues[i] == null) {//如果缓存为空则创建
                    smallValues[i] =
                        i < 10 ? new String(new char[]{DigitOnes[i]})
                               : new String(new char[]{DigitTens[i], DigitOnes[i]});
                }
            }
            return smallValues[i];
        }
	//如果不是在缓存的范围之内,则调用getChars方法,看到下面如果为负数时,size会加1,因为在getChars的最后两行要判断符号位的
        int size = negative ? stringSize(-i) + 1 : stringSize(i);
        char[] buf = new char[size];
        getChars(i, size, buf);
        // Android-changed: change string constructor.
        return new String(buf);
    }

还有一个类似的toString(int i, int radix)方法,不过上面返回的是十进制的,这个可以根据传的进制参数返回,比上一个要智能,看一下

    public static String toString(int i, int radix) {
	//如果radix小于2或者大于36,则返回10进制。Character.MIN_RADIX为2,Character.MAX_RADIX是36
	// public static final int MAX_RADIX = 36;
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
            radix = 10;

        /* Use the faster version */
        if (radix == 10) {//如果十进制调用上一个方法
            return toString(i);
        }

        char buf[] = new char[33];
        boolean negative = (i < 0);
        int charPos = 32;

        if (!negative) {// 如果不为负数,让i变为负数。
            i = -i;
        }

        while (i <= -radix) {
            int q = i / radix;
	    //这里radix * q - i相当于对i求余,如果理解不了可以把radix想象为10进制估计就好一些了
            buf[charPos--] = digits[radix * q - i];
            i = q;
        }
        buf[charPos] = digits[-i];

        if (negative) {// 如果为负数添加负号
            buf[--charPos] = '-';
        }

        return new String(buf, charPos, (33 - charPos));
    }

下面再看最后一个方法parseInt

    public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }

        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;// 根据最后一行的判断,result保存的是负数.
        boolean negative = false;//判断是否是负数
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;//正数的极限值
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
	    //这里主要判断如果是负数的操作。
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {// 是负数
                    negative = true;
                    limit = Integer.MIN_VALUE;//如果是负数,则极限值是Integer.MIN_VALUE
                } else if (firstChar != '+')//如果小于0,并且既不是"-"又不是"+"直接抛异常
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"//如果只是一个符号也抛异常
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {//数字不能小于0
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {//如果不判断,下面相乘会出现溢出,超过极限值,为什么要用小于,是因为result和multmin都是负数,
                    throw NumberFormatException.forInputString(s);
                }
		//逐个取出每一个字符与radix相乘,radix表示进制,比如2,8,10,10等,如果还是不好理解,就把radix想象为10进制吧。
                result *= radix;
                if (result < limit + digit) {//这里为什么要这样判断,是因为limit是负数,digit是正数,防止下面的操作导致结果溢出
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;//注意这里为什么要减,因为result是负数,在最后才进行符号判断
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;//如果知道result是负数,这里就好理解了
    }

OK,到现在为止Integer的所有方法基本都已分析完毕

猜你喜欢

转载自blog.csdn.net/abcdef314159/article/details/70176707