基础类型转化成String 转
基础类型转化成String
在程序中你可能时常会需要将别的类型转化成String,有时候可能是一些基础类型的值。在拼接字符串的时候,如果你有两个或者多个基础类型的值需要放到前面,你需要显式的将第一个值转化成String(不然的话像System.out.println(1+’a')会输出98,而不是”1a”)。当然了,有一组String.valueOf方法可以完成这个(或者是基础类型对应的包装类的方法),不过如果有更好的方法能少敲点代码的话,谁还会愿意这么写呢?
在基础类型前面拼接上一个空串(”"+1)是最简单的方法了。这个表达式的结果就是一个String,在这之后你就可以随意的进行字符串拼接操作了——编译器会自动将那些基础类型全转化成String的。
不幸的是,这是最糟糕的实现方法了。要想知道为什么,我们得先介绍下这个字符串拼接在Java里是如何处理的。如果一个字符串(不管是字面常量也好,或者是变量,方法调用的结果也好)后面跟着一个+号,再后面是任何的类型表达式:
| 1 | string_exp + any_exp | 
Java编译器会把它变成:
| 1 | newStringBuilder().append( string_exp ).append( any_exp ).toString() | 
如果表达式里有多个+号的话,后面相应也会多多几个StringBuilder.append的调用,最后才是toString方法。
StringBuilder(String)这个构造方法会分配一块16个字符的内存缓冲区。因此,如果后面拼接的字符不超过16的话,StringBuilder不需要再重新分配内存,不过如果超过16个字符的话StringBuilder会扩充自己的缓冲区。最后调用toString方法的时候,会拷贝StringBuilder里面的缓冲区,新生成一个String对象返回。
这意味着基础类型转化成String的时候,最糟糕的情况就是你得创建:一个StringBuilder对象,一个char[16]数组,一个String对象,一个能把输入值存进去的char[]数组。使用String.valueOf的话,至少StringBuilder对象省掉了。
有的时候或许你根本就不需要转化基础类型。比如,你正在解析一个字符串,它是用单引号分隔开的。最初你可能是这么写的:
| 1 | finalintnextComma = str.indexOf("'"); | 
或者是这样:
| 1 | finalintnextComma = str.indexOf('\''); | 
程序开发完了,需求变更了,需要支持任意的分隔符。当然了,你的第一反应是,得将这个分隔符存到一个String对象中,然后使用String.indexOf方法来进行拆分。我们假设有个预先配置好的分隔符就放到m_separator字段里(译注:能用这个变量名的,应该不是Java开发出身的吧。。)。那么,你解析的代码应该会是这样的:
| 1 2 3 4 5 6 7 8 9 10 11 12 | privatestaticList<String> split( finalString str ){    finalList<String> res = newArrayList<String>( 10);    intpos, prev = 0;    while( ( pos = str.indexOf( m_separator, prev ) ) != -1)    {        res.add( str.substring( prev, pos ) );        prev = pos + m_separator.length(); // start from next char after separator    }    res.add( str.substring( prev ) );    returnres;} | 
不过后面你发现这个分隔符就只有一个字符。在初始化的时候,你把String mseparator改成了char mseparator,然后把setter方法也一起改了。但你希望解析的方法不要改动太大(代码现在是好使的,我为什么要费劲去改它呢?):
| 1 2 3 4 5 6 7 8 9 10 11 12 | privatestaticList<String> split2( finalString str ){    finalList<String> res = newArrayList<String>( 10);    intpos, prev = 0;    while( ( pos = str.indexOf(""+ m_separatorChar, prev ) ) != -1)    {        res.add( str.substring( prev, pos ) );        prev = pos + 1; // start from next char after separator    }    res.add( str.substring( prev ) );    returnres;} | 
正如你所看到的,indexOf方法的调用被改动了,不过它还是新建出了一个字符串然后传递进去。当然,这么做是错的,因为还有一个indexOf方法是接收char类型而不是String类型的。我们用它来改写一下:
| 1 2 3 4 5 6 7 8 9 10 11 12 | privatestaticList<String> split3( finalString str ){    finalList<String> res = newArrayList<String>( 10);    intpos, prev = 0;    while( ( pos = str.indexOf(m_separatorChar, prev ) ) != -1)    {        res.add( str.substring( prev, pos ) );        prev = pos + 1; // start from next char after separator    }    res.add( str.substring( prev ) );    returnres;} | 
我们来用上面的三种实现来进行测试,将”abc,def,ghi,jkl,mno,pqr,stu,vwx,yz”这个串解析1000万次。下面是Java 641和715的运行时间。Java7由于它的String.substring方法线性复杂度的所以运行时间反而增加了。关于这个你可以参考下这里的资料。
可以看到的是,简单的一个重构,明显的缩短了分割字符串所需要的时间(split/split2->split3)。
| split | split2 | split3 | |
| Java 6 | 4.65 sec | 10.34 sec | 3.8 sec | 
| Java 7 | 6.72 sec | 8.29 sec | 4.37 sec | 
字符串拼接
本文当然也不能完全不提字符串拼接另外两种方法。第一种是String.concat,这个很少会用到。它内部其实是分配了一个char[],长度就是拼接后的字符串的长度,它将字符串的数据拷贝到里面,最后使用了私有的构造方法来生成了一个新的字符串,这个构造方法不会再对char[]进行拷贝,因此这个方法调用只创建了两个对象,一个是String本身,还有一个就是它内部的char[]。不幸的是,除非你只拼接两个字符串,这个方法才会比较高效一些。
还有一种方法就是使用StringBuilder类,以及它的一系列的append方法。如果你有很多要拼接的值的话,这个方法当然是最快的了。它在Java5中被首度引入,用来替代StringBuffer。它们的主要区别就是StringBuffer是线程安全的,而StringBuilder不是。不过你会经常并发的拼接字符串么难道?
在测试中,我们把0到100000之间的数全部进行了拼接,分别使用了String.concat, +操作符,还有StringBuilder,代码如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | String res = ""; for( inti = 0; i < ITERS; ++i ){    finalString s = Integer.toString( i );    res = res.concat( s ); //second option: res += s;}        //third option:        StringBuilder res = newStringBuilder(); for( inti = 0; i < ITERS; ++i ){    finalString s = Integer.toString( i );    res.append( s );} | 
| String.concat | + | StringBuilder.append | 
| 10.145 sec | 42.677 sec | 0.012 sec | 
结果非常明显——O(n)的时间复杂度明显要比O(n2) 要强得多。不过在实际工作中会用到大量的+操作符——因为它们实在是非常方便。为了解决这个问题,从Java6 update 20开始,引入了一个-XX:+OtimizeStringConcat开关。在Java 702和Java 715之间的版本,它是默认打开着的(在Java 6_41中还是默认关闭着的),因此可能你得手动将它打开。跟其它-XX的选项一样,它的文档也相当的差:
Optimize String concatenation operations where possible. (Introduced in Java 6 Update 20)
我们假设Oracle的工程师实现这个选项的时候是尽了最大努力的吧。坊间传闻,它是把一些StringBuilder拼接的逻辑替换成了类似String.concat那样的实现——它先生成一个合适大小的char[]然后再把东西拷贝进去。最后生成一个String。那些嵌套的拼接操作它可能也支持(str1 +(str2+str3) +str4)。打开这个选项后进行测试,结果表明,+号的性能跟String.concat的十分接近:
| String.concat | + | StringBuilder.append | 
| 10.19 sec | 10.722 sec | 0.013 sec | 
我们做另外一个测试。正如前面提到的,默认的StringBuilder构造器分配的是16个字符的缓冲区。当需要添加第17个字符时,这个缓冲区会被扩充。我们把100到100000间的数字分别追加到”12345678901234”的后面。结果串的长度应该是在17到20之间,因此默认的+操作符的实现会需要StringBuilder重新调整大小。作为对比,我们再做另一个测试,在这里我们直接创建一个StringBuilder(21)来保证它的缓冲区足够大,而不会重新调整:
| 1 2 | finalString s = BASE + i;finalString s = newStringBuilder( 21).append( BASE ).append( i ).toString(); | 
没有打开这个选项的话,+号的实现会比显式的StringBuilder的实现的时间要多出一半。打开了这个选项后,两边的结果是一样的。不过有趣的是,即使是StringBuilder的实现本身,打开了开关后速度居然也变快了!
| +, 开关关闭 | +, 开关打开 | new StringBuilder(21),开关关闭 | new StringBuilder(21),开关打开 | 
| 0.958 sec | 0.494 sec | 0.663 sec | 0.494 sec | 
总结
- 当转化成字符串的时候,应当避免使用”"串进行转化。使用合适的String.valueOf方法或者包装类的toString(value)方法。
- 尽量使用StringBuilder进行字符串拼接。检查下老旧码,把那些能替换掉的StringBuffer也替换成它。
- 使用Java 6 update 20引入的-XX:+OptimizeStringConcat选项来提高字符串拼接的性能。在最近的Java7的版本中已经默认打开了,不过在Java 6_41还是关闭的。
基础类型转化成String 转的更多相关文章
- double类型转化成string
		public static void main(String[] args) { double priceWithFreight = 1200.5698d; System.out.println(pr ... 
- JAVA基础部分复习(一、8中基础类型,以及String相关内容)
		以下是关于java中8种基本类型的介绍说明: package cn.review.day01; /** * java基础复习,8种数据类型 * (byte,short,long,int,double, ... 
- java基础类型数据与String类包装类之间的转换与理解
		数据类型转换一般分为三种: 在java中整型,实型,字符型视为简单数据类型,这些数据类型由低到高分别为:(byte,short,char--int-long-float-double) 简单数据类型之 ... 
- java对象转化成String类型
		在java项目的实际开发和应用中,常常需要用到将对象转为String这一基本功能.本文将对常用的转换方法进行一个总结.常用的方法有Object#toString(),(String)要转换的对象,St ... 
- Java基础-字符串连接运算符String link operator
		Java基础-字符串连接运算符String link operator 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 字符串链接运算符是通过“+”进行拼接的. 一.使用“+”进行字 ... 
- salesforce 零基础学习(五十八)通过sObject的field返回其对应的基础类型
		项目中有时候会要求通过sObject的Field的type类型返回其对应的基本类型,然后对其进行相关的处理,创建sObject的field可以选择的type类型是固定多的. 上述类型可以转换成几种基本 ... 
- TypeScript 素描-基础类型
		博文读自 TypeScript 官方文档而来,不具有学习性,仅是本人学习时记录以供日后翻阅 ,有学习TypeScript的朋友还请去看更为详细的官方文档 TypeScript官网文档中的基础类型, T ... 
- Typescript基础类型
		1.布尔值__boolean 2.数字__number----除了支持十进制和十六进制字面量,Typescript还支持ECMAScript 2015中引入的二进制和八进制字面量. 3.字符串__st ... 
- 玩转JavaScript OOP[0]——基础类型
		前言 long long ago,大家普遍地认为JavaScript就是做一些网页特效的.处理一些事件的.我身边有一些老顽固的.NET程序员仍然停留在这种认知上,他们觉得没有后端开发肯定是构建不了系统 ... 
随机推荐
- vue中解决拖动和点击事件的冲突
			BUG说明: 鼠标上下方向拖拽,如果松开时鼠标位于悬浮按钮上会默认执行click事件,经验证,click事件与mouse事件的执行顺序为onmousedown =>onmouseup => ... 
- The linux command 之进程
			******************查看进程********************* 一.使用ps命令 [me@linuxbox ~]$ ps PID TTY TIME CMD pts/ :: ba ... 
- python的update方法
			b = {"c":0, "position":{}} b["position"]["IF"] = {} print(b) ... 
- linux zip,tar压缩文件夹 忽略 .git 文件夾
			linux zip 忽略 .git 文件夾 # zip 命令 zip -r bitvolution.zip bitvolution -x *.git* # tar命令压缩文件夹忽略 .git文件夹 t ... 
- JAVA 设计模式之 工厂模式详解
			一.简单工厂模式 简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建出哪一种产品类 的实例.属于创建型模式,但它不属于 GOF,23 种设计模式 (参考资料: ht ... 
- vue中export和export default的使用
			1 export的使用 比喻index.js要使用test.js中的数据 首先在test.js文件中进行导出操作 在index.js文件进行导入操作 第一种方法: 此时的输出结果是: 注意: expo ... 
- python 集合(set)
			1.集合的创建 集合是一个无序不重复元素的集.基本功能包括关系测试和消除重复元素. 创建集合:大括号或 set() 函数可以用来创建集合.注意:想要创建空集合,你必须使用 set() 而不是 {},后 ... 
- Python3中面向对象 OOP
			Python3中面向对象 OOP 定义: python中通过关键字 class 实现类的定义: class ClassName(object): pass 获取成员变量:ClassName.变量名 修 ... 
- js 获取指定字符串个数
			参考:https://blog.csdn.net/maqinqin/article/details/5323824 function getStrCount(scrstr,armstr) { //sc ... 
- BZOJ1597: [Usaco2008 Mar]土地购买——斜率优化
			题目大意: 将$n$个长方形分成若干部分,每一部分的花费为部分中长方形的$max_长*max_宽$(不是$max_{长*宽}$),求最小花费 思路: 首先,可以被其他长方形包含的长方形可以删去 然后我 ... 
