java int转String全部方式的效率对比与深刻解析

  在java中,你们确定都会遇到int类型转String类型的情形,知其然知其因此然,总结加分析一下,int类型转String类型有如下几种方式:  javascript

  1. a+""
  2. String.valueOf(a)
  3. Integer.toString(a)
  以上三种方法在实际使用过程当中都是没有问题的,可是效率上仍是有些许差异的,因此写个小程序来对比一下他们的效率:

int a = 123456789;
long start = System.currentTimeMillis();
for (int i=0; i<100000; i++){
    String m = a+"";
}
long end = System.currentTimeMillis();
Log.e("time", "a+\"\" = " + (end - start));

start = System.currentTimeMillis();
for (int i=0; i<100000; i++){
    String n = String.valueOf(a);
}
end = System.currentTimeMillis();
Log.e("time", "String.valueOf(a) = " +(end-start));

start = System.currentTimeMillis();
for (int i=0; i<100000; i++){
    String n = Integer.toString(a);
}
end = System.currentTimeMillis();
Log.e("time", "Integer.toString(a) = " +(end-start));复制代码

最后打印出来的执行时间:html

E/time: a+"" = 257
E/time: String.valueOf(a) = 140
E/time: Integer.toString(a) = 159复制代码

能够看到在效率上除了a+""这种方式以外,其余两种方式的效率差很少,为何呢?看源码!java

String.valueOf(a) && Integer.toString(a)

  先看看后两种方式的源码:
String.valueOf(a)->Integer.toString(a)->IntegralToString.intToString(a)->convertInt(null, a)git

Integer.toString(a)->IntegralToString.intToString(a)->convertInt(null, a)
能够看到String.valueOf是经过调用Integer.toString实现的,也难怪他们的效率如此接近。他们最后都会调用到convertInt函数中:算法

private static String convertInt(AbstractStringBuilder sb, int i) {
    boolean negative = false;
    String quickResult = null;
    if (i < 0) {
        negative = true;
        i = -i;
        if (i < 100) {
            if (i < 0) {
                // If -n is still negative, n is Integer.MIN_VALUE
                quickResult = "-2147483648";
            } else {
                quickResult = SMALL_NEGATIVE_VALUES[i];
                if (quickResult == null) {
                    SMALL_NEGATIVE_VALUES[i] = quickResult =
                            i < 10 ? stringOf('-', ONES[i]) : stringOf('-', TENS[i], ONES[i]);
                }
            }
        }
    } else {
        if (i < 100) {
            quickResult = SMALL_NONNEGATIVE_VALUES[i];
            if (quickResult == null) {
                SMALL_NONNEGATIVE_VALUES[i] = quickResult =
                        i < 10 ? stringOf(ONES[i]) : stringOf(TENS[i], ONES[i]);
            }
        }
    }
    if (quickResult != null) {
        if (sb != null) {
            sb.append0(quickResult);
            return null;
        }
        return quickResult;
    }

    int bufLen = 11; // Max number of chars in result
    char[] buf = (sb != null) ? BUFFER.get() : new char[bufLen];
    int cursor = bufLen;

    // Calculate digits two-at-a-time till remaining digits fit in 16 bits
    while (i >= (1 << 16)) {
        // Compute q = n/100 and r = n % 100 as per "Hacker's Delight" 10-8
        int q = (int) ((0x51EB851FL * i) >>> 37);
        int r = i - 100*q;
        buf[--cursor] = ONES[r];
        buf[--cursor] = TENS[r];
        i = q;
    }

    // Calculate remaining digits one-at-a-time for performance
    while (i != 0) {
        // Compute q = n/10 and r = n % 10 as per "Hacker's Delight" 10-8
        int q = (0xCCCD * i) >>> 19;
        int r = i - 10*q;
        buf[--cursor] = DIGITS[r];
        i = q;
    }

    if (negative) {
        buf[--cursor] = '-';
    }

    if (sb != null) {
        sb.append0(buf, cursor, bufLen - cursor);
        return null;
    } else {
        return new String(cursor, bufLen - cursor, buf);
    }
}复制代码

分析一下,这个函数的工做主要能够分为这几步:小程序

  1. 若是a为负数,将a变成正数,若是a还小于0,直接置为Integer.MIN_VALUE;若是a小于100,则直接使用TENS和ONES数组进行快速计算得出结果,加上'-'号,直接返回该结果。
  2. 若是a为正数而且小于100,直接使用TENS和ONES数组进行快速计算得出结果返回。
  3. 若是上面两步没有处理完,说明a是大于100的数字,没法直接使用TENS和ONES数组进行快速计算,处理方式就是2位为一步循环处理,每次将这两位使用TENS和ONES数组进行快速计算得出这两位的结果存在数组的相应位置,直到只剩一位;最后剩下的一位使用DIGITS数组得出16进制的结果放在最后,返回结果。
  那么问题来了,当a>=100的时候,那两次while循环为何会使用0x51EB851FL和0xCCCD这两个数字呢?这个问题不要问我,我也不知道,不过源码做者注释写的很明白了:
// Compute q = n/100 and r = n % 100 as per "Hacker's Delight" 10-8
// Compute q = n/10 and r = n % 10 as per "Hacker's Delight" 10-8
去看 《Hacker's Delight》的10-8章。
  接着还有一个问题是TENS和ONES数组,直接看代码,一目了然:

/** TENS[i] contains the tens digit of the number i, 0 <= i <= 99. */
private static final char[] TENS = {
        '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'
};

/** Ones [i] contains the tens digit of the number i, 0 <= i <= 99. */
private static final char[] ONES = {
        '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',
};复制代码

每一个数组都是100的长度,都是用来处理0~99这100个数字,个位和十位的处理方式也很清楚。
  从代码角度来看,这个算法在数字小于100的和大于100的处理方式是不同的,小于100的快速计算法执行时间会远远短于大于100的方式,验证一下,将a变量修改成10:api

E/time: i+"" = 199
E/time: String.valueOf() = 7
E/time: Integer.toString() = 6复制代码

确实短了不少!!!数组

a+""

  再来看看a+""的方式,我认可这种方式我用的最多了,由于太简单了,java源码对'+'运算符进行了重载,源码我找不到啊,不过从网上找一些资料:oracle

The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings. String concatenation is implemented through the StringBuilder(or StringBuffer) class and its append method. String conversions are implemented through the method toString, defined by Object and inherited by all classes in Java. For additional information on string concatenation and conversion, see Gosling, Joy, and Steele, The Java Language Specification.app

地址:docs.oracle.com/javase/6/do…

能够看到,'+'运算符的主要方式是使用StringBuilder或者StringBuffer来实现的,相似于:

StringBuilder sb = new StringBuilder();
sb.append("");
sb.append(i);
String strI = sb.toString();复制代码

再来看看append的源码:
StringBuffer.append->IntegralToString.appendInt(this, a)->convertInt(sb, i)
能够看到'+'运算符最后也是调用到了同一个函数,只不过第一个参数的sb不为null而已,因此已经很清楚了,'+'运算符的执行效率不高的缘由应该就在以前的new StringBuilder等操做和以后的StringBuilder.toString等操做,反编译class文件也能够得出同样的结论:
stackoverflow.com/a/4105406
  因此a+""的方式之后就少用一点了,效率不高,也显得不太专业。

扩展

  String 扩展的相关知识:
常量池的内存分配在 JDK六、七、8中有不一样的实现:

  1. JDK6及以前版本中,常量池的内存在永久代PermGen进行分配,因此常量池会受到PermGen内存大小的限制。
  2. JDK7中,常量池的内存在Java堆上进行分配,意味着常量池不受固定大小的限制了。
  3. JDK8中,虚拟机团队移除了永久代PermGen。
    关于永久代移除:www.infoq.com/cn/articles…
    例子1:
    public class StringTest {
     public static void main(String[] args) {
         String a = "java";
         String b = "java";
         String c = "ja" + "va";
     }
    }复制代码
    变量 a、b 和 c 都指向常量池的 “java” 字符串,表达式 “ja” + “va” 在编译期间会把结果值”java”直接赋值给c,因此最终的结果 a==c 为 true。

例子2:

public class StringTest {
    public static void main(String[] args) {
        String a = "hello ";
        String b = "world";
        String c = a + b;
        String d = "hello world";
    }
}复制代码

咱们根据上面知道在 java 中 “+” 运算符其实是使用 StringBuilder.append 去实现的,因此此时会在 Java 堆上新建一个 String 对象,这个 String 对象最终指向常量池的 "hello world",因此说此时 c==d 为 false。
不过有种特殊状况,当final修饰的变量发生链接动做时,编译器会进行优化,将表达式结果直接赋值给目标变量:

public class StringTest {
    public static void main(String[] args) {
        final String a = "hello ";
        final String b = "world";
        String c = a + b;
        String d = "hello world";
    }
}复制代码

因此此时 c==d 为 true。

引用

www.importnew.com/21711.html
www.importnew.com/21720.html

相关文章
相关标签/搜索