Integer源码解析

前言

今天我们来分析一下Integer源码。

Integer是八种包装类里面的比较常用的一种。那在使用时有什么注意及学习的地方呢?

让我们一起来看一下

分析

Integer是包java.lang下的一个类。

public final class Integer extends Number implements Comparable<Integer> {
//code
}

其被定义成final类型,继承Number类实现Comparable接口。

@Native public static final int   MIN_VALUE = 0x80000000;
@Native public static final int   MAX_VALUE = 0x7fffffff;
public static final Class  TYPE = (Class) Class.getPrimitiveClass("int");

可以看出,其定义了Integer的最大值为2^31-1,最小值为-2^31。Integer的基本数据类型为int。

我们来看一下Integer的toString方法,是比较有趣的。

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

方法中使用到了stringSize函数,就是求这个Integer数的长度,我们来看看他是如何实现的。

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

可以看到这段代码在计算Integer数长度时,构建了一个一维数组,然后拿x与数组每个值进行比较。而未使用我们经常说的除法或乘法计算长度。我们可以看下源码里的注释。

// I use the "invariant division by multiplication" trick to
// accelerate Integer.toString.  In particular we want to
// avoid division by 10.
//
// The "trick" has roughly the same performance characteristics
// as the "classic" Integer.toString code on a non-JIT VM.
// The trick avoids .rem and .div calls but has a longer code
// path and is thus dominated by dispatch overhead.  In the
// JIT case the dispatch overhead doesn't exist and the
// "trick" is considerably faster than the classic code.
//
// TODO-FIXME: convert (x * 52429) into the equiv shift-add
// sequence.
//
// RE:  Division by Invariant Integers using Multiplication
//      T Gralund, P Montgomery
//      ACM PLDI 1994

我们知道计算机在计算除法效率要比加减乘法低。所以为了避免除法,提高计算效率,采用此种方法。

正好我们可以看看Long的toString方法里的stringSize方法。

    // Requires positive x
    static int stringSize(long x) {
        long p = 10;
        for (int i=1; i<19; i++) {
            if (x < p)
                return i;
            p = 10*p;
        }
        return 19;
    }

可以看到使用了乘法。你或许会问为什么没有像Integer那样构建一个数组去比较?额,如果要构造数组,那要构造一个19位的数组,里面有1-19位的数,代码写起来很多很臃肿吧,而且构造好的数组会长期放在内存中,我们知道,在实际应用中,Integer的使用频率要比Long高多了,长期让Long里面的一个数组占据内存空间也不太合理。以上是我个人见解。

我们再来看一下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;
        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;
                } else if (firstChar != '+')
                    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) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }

我们可以看到此方法首先进行异常处理,然后判断传入String是否有正负号,然后截取位数,使用乘法,用减法得到int值,然后判断正负并返回结果。

我们再来看下Integer的内部类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;
            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() {}
    }

很容易理解这段代码,初始化Integer后,IntegerCache会缓存[-128,127]之间的数据,这个区间的上限可以配置,取决于java.lang.Integer.IntegerCache.high这个属性,这个属性在VM参数里为-XX:AutoBoxCacheMax=2000进行设置调整或者VM里设置-Djava.lang.Integer.IntegerCache.high=2000。所以Integer在初始化完成后会缓存[-128,max]之间的数据。

并且我们可以看到valueOf方法。

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

可以看到valueOf方法,在cache范围内,返回的是缓存的值,是相同的对象,不在cache范围内,才会新建Integer。

由于有了Integer缓存,我们可以测试以下代码。

Integer a=1;
Integer b=1;
Integer c=new Integer(1);
Integer d=1000;
Integer e=1000;
System.out.println(a==b);
System.out.println(b==c);
System.out.println(d==e);

可以看到结果为true,false,false。

这样,我们在比较Integer时,如果仅仅比较值相等,建议使用equals方法比较。

我们可以看下Integer的equals方法。

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

可以看到它会先判断类型是否符合,然后进行拆箱比较操作。

同样,在Long,Byte,Short,我们也可以看到缓存,其缓存数据长度均是-128到127。

    //Long
    private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }
    //Byte
    private static class ByteCache {
        private ByteCache(){}

        static final Byte cache[] = new Byte[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }
    //Short
    private static class ShortCache {
        private ShortCache(){}

        static final Short cache[] = new Short[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Short((short)(i - 128));
        }
    }

关于缓存的意义:在该范围内数据比较常用,添加缓存提高性能。不用每次都新建,浪费系统资源。

同时根据Integer的hashCode方法,我们可以看到,Integer的hashCode返回本身的int值。

    @Override
    public int hashCode() {
        return Integer.hashCode(value);
    }

结论

以上就是Integer的源码分析,可以看到,对于偏底层的一些调用频繁的类,Java都做了很多方面的优化。包括从性能及内存开销等诸多方面。是值得我们学习和理解的。

你可能感兴趣的:(JAVA)