解读Integer类的parseInt和valueOf的区别

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

解读Integer类的parseInt和valueOf的区别

bai_student   2022-11-23 我要评论

Integer类的parseInt和valueOf区别

我们平时应该都用过或者见过parseInt和valueOf这两个方法。一般我们是想把String类型的字符数字转成int类型。从这个功能层面来说,这两个方法都一样,都可以胜任这个功能。

但是,我们进入源码,看下Integer类下这两个方法

我们看parseInt()这个方法是如何实现的

public static int parseInt(String s) throws NumberFormatException {
    return parseInt(s,10);
}

我们再看valueOf()是如何实现的

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

从代码,我们起码看到了两点:返回结果类型不一样,parseInt方法返回的是int基本类型,valueOf方法返回的是Integer的包装类型

valueOf方法实际上是调用了parseInt方法,也就是说,如果我们仅仅只需要得到字符串类型字符数值对应的整数数值,那我们大可不必调用valueOf,因为这样得到整形数值之后还要做一个装箱的操作,将int封装为Integer。

写代码测试效率:

public class StringDemo {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str = "123";
        long startTime = System.currentTimeMillis();
        for(int i = 0;i<100000000;i++){
            Integer.parseInt(str);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
}

如下代码:

public class StringDemo {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str = "123";
        long startTime = System.currentTimeMillis();
        for(int i = 0;i<100000000;i++){
            Integer.valueOf(str);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
}

分别测试三遍,得到的时间如下,可以看到paraseInt()的效率更好。

方法第一次时长第二次时长第三次时长
parseInt()294629652952
valueOf()312431173126

Integer的parseInt与value of原理

我一直使用Integer的转换,包括Long,枚举等,从来没有注意它是怎么实现的,最近有个业务组转换报错了,想看看是如何实现的。据笔者猜测:ASCII码转换?这是常用的计量,什么大写变小写都是这样实现的。下面看看如何实现的吧

1. demo构建

public class StringParseInt {
    public static void main(String[] args) {
        String str = "-1234";
        int i = Integer.parseInt(str);
        int y = Integer.valueOf(str);
 
        System.out.println(i + "\t" + y);
    }
}

输出都正常,关键是看怎么实现的

2. Integer的实现方式

2.1 value of 

    public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }

本质还是parseInt,从这点看与parseInt没有区别;但是这里有装箱,如果接收值是int建议直接使用parseInt,省去装箱的过程。

    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 {
        //下限固定-128
        static final int low = -128;
        //上限没有初始化
        static final int high;
        //核心缓存数组
        static final Integer cache[];
        //类加载初始化
        static {
            // high value may be configured by property
            //初始127上限
            int h = 127;
            //VM参数java.lang.Integer.IntegerCache.high可以配置Integer的最大上限
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    //我没设置的值,如果不是int字符串型就会报错,然后被捕获
                    int i = parseInt(integerCacheHighPropValue);
                    //取大,对比127;也就是至少是127
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    // 上面官方注释很明显了,这里减128再减1是因为low是-128
                    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;
 
            //创建缓存数组,如果设置java.lang.Integer.IntegerCache.high,不宜设置过大,过大很占连续空间
            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)
            //断言至少127,JLS7
            assert IntegerCache.high >= 127;
        }
 
        private IntegerCache() {}
    }

从源码看Integer内部类装载时,会初始化一个缓存空间,存储Integer对象。很多面试时就会被这个坑了,初始化的缓存对象地址取值是一致的,可以使用==作对比;然后超过这个范围的Integer就不能了,要使用Integer的eq方法

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

因为自动装箱,实际上使用的value of方法,默认情况下,-128~127使用缓存对象。 

2.2 parseInt

    /**
     * 这段注释尤为重要,定义了符号位,定义了10进制数字
     * Parses the string argument as a signed decimal integer. The
     * characters in the string must all be decimal digits, except
     * that the first character may be an ASCII minus sign {@code '-'}
     * ({@code '\u005Cu002D'}) to indicate a negative value or an
     * ASCII plus sign {@code '+'} ({@code '\u005Cu002B'}) to
     * indicate a positive value. The resulting integer value is
     * returned, exactly as if the argument and the radix 10 were
     * given as arguments to the {@link #parseInt(java.lang.String,
     * int)} method.
     *
     * @param s    a {@code String} containing the {@code int}
     *             representation to be parsed
     * @return     the integer value represented by the argument in decimal.
     * @exception  NumberFormatException  if the string does not contain a
     *               parsable integer.
     */    
    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }

重点来哦,String能转为int的本质,这里需要传一个核心参数,Integer给我们默认了10进制,其他类型也是相同,比如Long

    public static long parseLong(String s) throws NumberFormatException {
        return parseLong(s, 10);
    }

因为转换后的就是10进制的数字,可供使用。

进一步分析parseInt(String s, int radix);radix即进制的意思。

    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.
         */
        //字符串不能为null,没有判断空字符串
        if (s == null) {
            throw new NumberFormatException("null");
        }
        //进制不能小于2,至少要2进制
        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }
        //进制不能大于36
        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);
            //这里玩了个计谋,0字符的ASCII是48,后面的数字包括ABCDEF的ASCII都比0大;
            //其中 + 43; - 45
            //只有带符号位的会判断,其他就默认正数
            if (firstChar < '0') { // Possible leading "+" or "-"
                //负数
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                //非负即正,因为小于'0'
                } 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
                // 拿到字符,转换为ASCII数字并按进制转为数字
                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;
    }

这里说一下Character.digit(s.charAt(i++),radix)

    public static int digit(char ch, int radix) {
        return digit((int)ch, radix);
    }

digit就是数字的意思,这里直接把char字符强转int类型,即ASCII数字,然后按照进制转换成相应的数字 

小结:类型转换其实是字符的ASCII的解析符号位,并按字符转为数字,然后使用逆向负进位方式生成数字,最后修正符号得出我们想要的结果。算法符合了字符串解析的顺序,不符合人类的思维习惯。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们