读书笔记--编写高质量代码:改善java程序的151个建议(四)String,StringBuilder,StringBuffer
使用字符串字面量赋值更加高效。
Java为了避免在一个系统中大量产生String对象,于是就设计了一个字符串池(也有叫做字符串常量池),在字符串池中所容纳的都是String字符串对象。
String创建规则:创建一个字符串时,首先没有检查池中是否有字面值相等的字符串。
如果有,则不再创建,直接返回池中该对象的引用。
如果没有则创建之,然后放到池中,并返回新建对象的引用。
注释字符串操作时的位置
例如代码:
public class Client {
public static void main(String[] args) {
String str1 = 1+2+"apples";
String str2 = "apples:"+1+2;
}
}
str1=3apples,str2=apples:12
正确使用String、StringBuffer、StringBuilder
CharSequence接口有三个实现类与字符串有关:String、StringBuffer、StringBuilder,虽然他们都与字符串有关,但是其处理机制是不同的。
String类是不可改变的(immutable)量,也就是创建后就不能再修改了.即使想通过Sting提供的方法来尝试修改,也是要么创建一个新的字符串对象,要么返回自己。string的substring(0)时返回自己。
StringBuffer是一个可变字符序列,它与String一样,在内存中保存的都是一个有序的字符序列(char类型的数组),不同点是StringBuffer对戏那个的值是可改变的。例如append()方法。
StringBuilder与StringBuffer基本相同,都是可变字符序列,不同点是:StringBuffer是线程安全的,StringBuilder是线程不安全的,查看源码可以发现StringBuffer的方法前都有synchronized关键字,这也是StringBuffer在性能上远低于StringBuilder的原因。
在性能方面,由于String类的操作都是产生新的String对象,而StringBuilder和StringBuilder只是一个字符数组的在扩容而已,所以String类的操作要远慢于StringBuffer和StringBuilder.
三者的使用场景
_ 使用String类的场景 在字符串不经常变化的场景中可以使用String类,例如常量的声明,少量的变量运算等。 _ 使用StringBuffer类的场景 在频繁惊醒字符串的元算(如拼接、替换、删除等),并且运行在多线程的环境中,则可以考虑使用StringBuffer,例如XML的解析,HTTP参数解析和封装等。 _ 使用StringBuilder类的场景。 和StringBuffer类似,只是运行在单线程环境中。
自由选择字符串拼接方法
+方法拼接字符串
虽然编译器对字符串的加号做了优化,它会使用StringBuilder的append方法进行追加,其原理如下:
public class Client {
public static void main(String[] args) {
String str = "abc";
//str = str+"d";
str = new StringBuilder(str).append("d").toString();
}
}
+方法每次操作都会创建一个StringBuilder对象,而且都会执行一次toString 这两个步骤比较耗时。
concat方法拼接字符串
concat的源代码如下:
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
char buf[] = new char[count + otherLen];
getChars(0, count, buf, 0);
str.getChars(0, otherLen, buf, count);
return new String(0, count + otherLen, buf);
}
整体上看就是一个数组拷贝,虽然在内存中的处理都是原子性操作,速度非常快,不过,最后的return语句表明每次concat操作都会创建一个String对象,这就是concat比较耗时操作。
append方法拼接字符串
StringBuilder的append方法直接有父类AbstractStringBuilder实现,其代码如下:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
}
整个append方法都在做字符数组处理,加长,然后数组拷贝,这些都是基本的数据处理,没有创建任何对象,所以速度最快了。
三者的实现方法不同,性能也就不同,但并不表示我们一定要使用StringBuilder,这是因为+非常符合我们的编码习惯,适合人类阅读,两个字符串拼接,就用加号连一下,这很正常,也很有号,在大多数情况下我们都可以使用加号操作,只有在系统性能临界到时候才可以考虑使用concat或者append方法。而且,很多时候系统的80%的性能是消耗在20%的代码上的,我们的精力应该更多的投入到算法和结构上。
在复杂的字符串操作中推荐使用正则表达式
这个没什么好说的,正则表达式是恶魔,威力巨大,但难以控制。
强烈建议使用UTF编码
不想项目出现乱码问题就使用UTF-8格式编码。
字符串排序
String的compareTo的排序实现是按照英文字母表的顺序,想排序中文的话推荐使用Collator类进行排序例如:
public class Client {
public static void main(String[] args) {
Comparator<Object> cmp=Collator.getInstance(java.util.Locale.CHINA);//其中getinstance为获得所需要的语言环境
String []str={"张三","阿门","别赛男","神仙","逍遥"};
Arrays.sort(str, cmp);
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
}
}
Collator的一个缺点是对一些偏僻字支持不好,但是也能满足一般的需求了。