JDK源码分析----Integer

1. Integer的缓存技术


JDK1.5后Java引入了自动装箱和自动拆箱技术,

Integer ina = 2;
Integer inb = 2;
System.out.println("装箱后 "+(ina == inb));
Integer inc = 200;
Integer ind = 200;
System.out.println("装箱后"+(inc == ind));

上面的执行结果分别为true和false.为什么两次装箱结果不同呢?这是由于java的Integer在实现时有这样一个内部类,

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

不难发现,-128到127之间的整数会在第一次使用时(类加载时)被放在一个叫cache的数组里。当然这个大小区间可以改变JVM的设置来改变。

并且,Integer的静态方法valueOf(int i)也使用了该数组。

   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);
    }

2. 一个整数在给定进制的字符串表示


toString(int i , int radix)

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

		while(i >= radix)
		{
			buf[charPos--] = digits[i%radix];
			i = i / radix;
		}
		buf[charPos] = digits[i];
		if(negative)
		{
			buf[--charPos] = '-';
		}
		return new String(buf, charPos, (33 - charPos));

以上是核心代码,实际上在JDK源码里是统一化成负数处理。笔者觉得这不符合习惯,此次改为统一化为正数处理。至于这里为什么数组长度为33也是明显的,因为java里int有32位,加上可能的符号位。

3. 一个整数的长度


static int stringSize(int x)

这个函数不是个public权限的函数,作为内部工具方法使用。

    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;
    }

这个方法的实现是很巧妙的,避免除法、求余等,判断条件简单,效率高(采用静态field分析,而不是负责逻辑判断可以明显提高效果)。(int 最大长只有10)

4. toString()的实现


可能有人觉得上面的toString(int i, int radix)已经是通用算法了,但是JDK在并没有这样(即radix是10的情况),而是采用了效率更高的方法。

   public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
    }

这个算法的核心是getChars的实现,即将一个整数高效地逐位存入一个char数组中。

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

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

   while (i >= 65536) {
            q = i / 100;
        // really: r = i - (q * 100);
            r = i - ((q << 6) + (q << 5) + (q << 2));
            i = q;
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }

这个处理大数部分,其中DigitOnes和DigitTens分别如下,

    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',
        } ;

    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',
        } ;

上面while部分的思想是,DigitOnes是代表个位,DigitTens代表十位,每次r可以迭代两位(r就是除以100的余数)。至于移位运算,是为了提高运算速度,q*100 = q*(2^6) + q*(2^5)  + q*(2^2) = 64q+32q+4q.

然后对小数采用更快速的方式,

    for (;;) {
            q = (i * 52429) >>> (16+3);
            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
            buf [--charPos] = digits [r];
            i = q;
            if (i == 0) break;
        }

上面操作的作用是q得到i截断个位的值.(q = i / 10 ).至于采用上述复杂的移位的目的是提高速度(>>>无符号右移).


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值