java int转String全部方式的效率对照与深入解析
在java中,大家肯定都会遇到int类型转String类型的情形,知其然知其所以然。总结加分析一下,int类型转String类型有下面几种方式:
- a+”“
- String.valueOf(a)
- 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));
最后打印出来的运行时间:
E/time: a+"" = 257
E/time: String.valueOf(a) = 140
E/time: Integer.toString(a) = 159
能够看到在效率上除了a+”“这样的方式之外,其它两种方式的效率差点儿相同。为什么呢?看源代码!
String.valueOf(a) && Integer.toString(a)
先看看后两种方式的源代码:
String.valueOf(a)->Integer.toString(a)->IntegralToString.intToString(a)->convertInt(null, a)
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);
}
}
分析一下,这个函数的工作主要能够分为这几步:
- 假设a为负数。将a变成正数,假设a还小于0,直接置为Integer.MIN_VALUE;假设a小于100。则直接使用TENS和ONES数组进行高速计算得出结果。加上’-‘号。直接返回该结果。
- 假设a为正数而且小于100。直接使用TENS和ONES数组进行高速计算得出结果返回。
- 假设上面两步没有处理完,说明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:
E/time: i+"" = 199
E/time: String.valueOf() = 7
E/time: Integer.toString() = 6
确实短了非常多。!!
a+”“
再来看看a+”“的方式,我承认这样的方式我用的最多了,由于太简单了,java源代码对’+’运算符进行了重载。源代码我找不到啊,只是从网上找一些资料:
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.
地址:http://docs.oracle.com/javase/6/docs/api/java/lang/String.html
能够看到,’+’运算符的主要方式是使用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文件也能够得出一样的结论:
http://stackoverflow.com/a/4105406。
所以a+”“的方式以后就少用一点了,效率不高,也显得不太专业。
扩展
String 扩展的相关知识:
常量池的内存分配在 JDK6、7、8中有不同的实现:
1. JDK6及之前版本号中,常量池的内存在永久代PermGen进行分配。所以常量池会受到PermGen内存大小的限制。
2. JDK7中,常量池的内存在Java堆上进行分配。意味着常量池不受固定大小的限制了。
3. JDK8中,虚拟机团队移除了永久代PermGen。
关于永久代移除:http://www.infoq.com/cn/articles/Java-PERMGEN-Removed
样例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。
引用
http://www.importnew.com/21711.html
http://www.importnew.com/21720.html
java int转String全部方式的效率对照与深入解析的更多相关文章
- java int and string convert
int -> String int i=12345; String s=""; 第一种方法:s=i+""; 第二种方法:s=String.valueOf( ...
- java中多种写文件方式的效率对比实验
一.实验背景 最近在考虑一个问题:“如果快速地向文件中写入数据”,java提供了多种文件写入的方式,效率上各有异同,基本上可以分为如下三大类:字节流输出.字符流输出.内存文件映射输出.前两种又可以分为 ...
- Java int与String互相转化大全
int -> String //int 转化才 string int num = 123456; //方法一 会产生两个String对象 String s1 = num+"" ...
- Java -- int与String相互转换
int转换为String 使用Integer的静态方法 Integer.toString(int num); 空值会抛出NullPointerException异常 使用String的静态方法 Str ...
- java int和String类型之间的相互转换
String --> int 第一种方法:int i = Integer.parseInt(s); 第二种方法:int i = Integer.valueOf(s).intValue(); 两种 ...
- java Int 和 String 之间的转换
String 转换成 int Integer.parseInt(formParams.get("id")) int 转换成 string Integer.toString(id);
- Java int转string 长度不足左补0
最近项目中用到这个需求,我试了两个方法都可以实现 方法一:(推荐) String s=String.format("%010d", 123)//123为int类型,0代表前面要补的 ...
- Java int to String互转
Integer.toString Integer.parseInt(lAyaNums);
- C#的StringBuilder 以及string字符串拼接的效率对照
今天公司一个做Unity3d的人在说字符串拼接的一个效率问题,他觉得string拼接会产生新的一个内存空间,假设不及时回收会产生大量的碎片,特别是在Unity3d这样一个Updata环境下,由于每一帧 ...
随机推荐
- 牛客小白月赛4——H-相邻的糖果
链接:https://www.nowcoder.com/acm/contest/134/H来源:牛客网 题目描述 有n个盒子摆成一排,每个盒子内都有ai个糖果. 现在你可以执行以下操作: ·你可以选择 ...
- windows批处理命令学习
初入批处理的学习,发现运行window的系统命令会减少很多的劳动,很方便. 参考例子: http://www.jb51.net/article/41322.htm
- Topcoder SRM 603 div1题解
昨天刚打了一场codeforces...困死了...不过赶在睡前终于做完了- 话说这好像是我第一次做250-500-1000的标配耶--- Easy(250pts): 题目大意:有一棵树,一共n个节点 ...
- noi2017 T1 整数 ——线段树
loj.ac上有 题目传送门 不过我还是把题目搬过来吧 整数(integer)[题目背景]在人类智慧的山巅,有着一台字长为 1048576 位的超级计算机,著名理论计算机科 学家 P 博士正用它进行 ...
- AngularJS 作用域与数据绑定机制
AngularJS 简介 AngularJS 是由 Google 发起的一款开源的前端 MVC 脚本框架,既适合做普通 WEB 应用也可以做 SPA(单页面应用,所有的用户操作都在一个页面中完成).与 ...
- Centos 7 ssh登录速度慢
在server上/etc/hosts文件中把你本机的ip和hostname加入 hostname ifconifg 在server上/etc/ssh/sshd_config文件中修改或加入UseDNS ...
- Android BroadcastReceiver使用
1. 介绍 BroadcastReceiver用于接收广播事件(Intent), 是Android四大组件之一 2. 使用 2.1 实现广播接收类 实现一个广播收类继承BroadcastReceiv ...
- EditText双光标问题
模拟器会出现中双的光标 从没有字符开始输入多了一个竖线怎么回事?光标丢失就好了,下面是手机情况 修改样式更换一个样式试试看:比如我以前的是 android:theme="@android ...
- 跨平台的EVENT事件 windows linux
#ifndef _HIK_EVENT_H_ #define _HIK_EVENT_H_ #ifdef _MSC_VER #include <Windows.h> #define hik_e ...
- python--enum
# enum用于枚举,该模块下有一个Enum,我们定义的类要继承它 # 一旦继承,那么我们定义的key(仮),不能有重复值. # 如果要保证value(仮)不重复,那就引入unique,给我们定义的类 ...