JavaString、StringBuilder、StringBuffer总结

来源:互联网 发布:新东方英语网络视频 编辑:程序博客网 时间:2024/05/16 01:15

背景:

       最近项目中需要用到服务器模板和字符串拼接技术。服务器模板技术很多,JSP、Velocity、JDynamiTe等很多。字符串拼接技术在Java中更简单,StringBuilder、StringBuffer和重载的字符串“+”操作。但是实际开发中,发现自己平时对Java的字符串拼接的细节处理真的很差。

 

基础:

       字符串操作主要问题在效率上,包括如下两点:

       (1)拼接效率。

       (2)查找、回溯效率。

       凡是了解Java的都知道下面3点,而且这肯定是Java面试时必考的。

       (1)StringBuilder是线程不安全的;

       (2)StringBuffer是线程安全的;

       (3)Java的String是final的,而且Java的String对象是放在字符串常量池的。

 

       其实在这之前,我对这几点也只是“知其然而不知其所以然”,因为懒惰的关系,这三个类的源码也没看过,今天终于有时间看了下,顺便总结下。

 

继承体系

       这个三个类的继承体系如下:

 

       (1)他们都实现了CharSequence接口,而且都是可以被序列化、被比较大小的。

       (2)阅读它们的源码可以知道,他们在管理字符串时,都使用的char数组。其实字符串本身就是一种数据结构,和Stack、Queue一样,只是String数据结构太常见了,才导致我们已经不把它当成一种数据结构了。

       (3)StringBuilder和StringBuffer有一个共同的抽象父类AbstractStringBuilder,凡是对StringBuilder的方法调用,在StringBuilder内部都是调用的super方法,StringBuffer也一样,只不过StringBuffer在自己的方法上加上了synchronized关键字。

 

append方法

       我们对StringBuilder和StringBuffer调用最多的应该就是append方法了,append方法有很多重载方法,这些重载方法的实现思路都是一样的,首先把传入的参数转换为char数组,然后把char数组和自己本身的数组进行合并,至于合并算法就很简单了(不过可以注意下合并后char数组的长度)。

       不同类型的参数转换为char数组的方式略有不同,Object类型调用String的静态方法valueOf,基本类型的除了char和boolean外,都是调用的他们类型本身的getChars静态方法。char添加比其他类型略快,不用调用getChars方法,boolean则直接添加一个[‘t’,’r’,’u’,’e’]或者[‘f’,’a’,’l’,’s’,’e’]字符数组。

       所以从上面可以看出,调用append方法的时候如果可以直接append一个char或者char数组是最好的。

 

String

       接着说下String,我们都知道JVM本身是对String对象有过优化的,而且实现了对String的操作符(+)的重载。JVM把String对象都放在常量池中,每次new对象的时候,都先去检查常量池存不存在一个相同的字符串序列,如果存在序列相同的就返回这个对象,如果不存在则创建一个新的。

       有一个不负责任的笼统说法:JVM对String + 的重载其实是在编译阶段把+ 翻译成了StringBuilder(JDK 1.4前是StringBuffer)。


比如:

[java] view plaincopy
  1. String s = “a”;  
  2. String m = s + “a” + “d”;  

编译后就是:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. String s =”a”;  
  2. String m = (new StringBuilder(s)).append(“a”).append(“d”).toString();  


       所以,根据这个规律我们在for循环中使用+操作效率是比较低的。

       当然上面的情况是有特例的,毕竟Java编译机制不会这么简单,比如当Java编译器在编译时能确定字符串的值时就不会遵循上面的编译规则。

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. String s= “a”;  
  2. String m = “s” + “x” + “e”;  

       这个时候m的值可以被确定,因为没有变量参与运算,就可以直接赋值为sxe字符串对象。

 

总结:

       在构造StringBuilder的时候能确定序列长度,最好指定序列长度,这样能减少append时的数组copy。

       在使用StringBuilder的时候如果可以尽量使用append字符的方式,如:append(‘a’)比append(“a”)要好,而且不会产生字符串a的对象。但是也不要特意这样,比如append(‘a’).append(‘b’).append(‘c’)替代append(“abc”),那就有点画蛇添足了。

       其次,在append的参数中少用字符串+操作,如果大量的使用字符串+操作有的时候会失去使用StringBuilder的意义。比如

       append(“a”+s+”d”);

应该拆分为

       append(‘a’).append(s).append(‘d’);

       这样在大量循环中,能帮我们提高效率。

       最后,关于字符串的查找和回溯操作,如果可以,我们可以直接使用索引的方式去截取字符串也就是操作String类的API,这比在Java中使用正则表达式要好很多。


0 0
原创粉丝点击