提出问题

项目中字符串操作应该注意的一些性能问题???

解决问题

以下来自《Java程序性能优化》这本书中的一点总结,以及自己的想法,每篇文章都很短,不太喜欢写太多,一步一步慢慢学就可以了:

String类的基本实现:char数组,offset偏移量和String的count长度

对String优化主要表现在3个方面,同时也是String对象的3个特点:

  • 不变性:String对象一旦生成,则不能再对它进行改变。
  • 针对常量池的优化。
  • 类的final定义。

例一:

  1. @Test
  2. public void testString(){
  3. String boy = "boy";
  4. String girl = "girl";
  5. String girl2 = "girl";
  6. String girl3 = new String("girl");
  7. System.out.println(boy == girl);//false
  8. System.out.println(girl == girl2);//true
  9. System.out.println(girl == girl3);//false
  10. System.out.println(girl == girl3.intern());//true
  11. }

subString内存泄露问题

具体可以看高手文章:

http://www.cnblogs.com/techyc/p/3324021.html

在jdk6中,substring还是指向原来的字符串,可以用 new String(str.substring())避免内存泄露,
在jdk7中,没有这个问题

字符串分割和查找

使用效率更高的StringTokenizer类分割字符

例二:

  1. @Test
  2. public void testString(){
  3. String originalStr = null;
  4. StringBuffer sb = new StringBuffer();
  5. for(int i=0;i<10000;i++){
  6. sb.append("ay");
  7. sb.append(";");
  8. }
  9. originalStr = sb.toString();
  10. long startTime = System.currentTimeMillis();
  11. for(int i=0;i<10000;i++){
  12. //在这里分割10000次
  13. originalStr.split(";");
  14. }
  15. System.out.println(System.currentTimeMillis() - startTime);
  16. }

结果花费:14920ms(我电脑比较 lan )

性能改造:

  1. @Test
  2. public void testString(){
  3. String originalStr = null;
  4. StringBuffer sb = new StringBuffer();
  5. for(int i=0;i<10000;i++){
  6. sb.append("ay");
  7. sb.append(";");
  8. }
  9. originalStr = sb.toString();
  10. long startTime = System.currentTimeMillis();
  11. StringTokenizer tokenizer = new StringTokenizer(originalStr,";");
  12. for(int i=0;i<10000;i++){
  13. while (tokenizer.hasMoreTokens()){
  14. tokenizer.nextToken();
  15. }
  16. }
  17. System.out.println(System.currentTimeMillis() - startTime);
  18. }

结果花费:8ms(我电脑比较 lan ) 这不是一个等级的啊!get起来

高效率的charAt()方法

charAt()效率比startsWith()或者endsWith效率高,对于判断单个字符开头的还可以,对于多个的话,我测试下,效率并没有高很多。

例三:

  1. @Test
  2. public void testString3(){
  3. String loveStr = "Love you!!!";
  4. long startTime = System.currentTimeMillis();
  5. //执行100万次
  6. for(int i=0;i<1000000;i++){
  7. //判断是否以L开头
  8. if(loveStr.charAt(0) == 'L'
  9. ){
  10. }
  11. }
  12. System.out.println(System.currentTimeMillis() - startTime);
  13. }

结果花费:20ms

  1. @Test
  2. public void testString4(){
  3. String loveStr = "Love you!!!";
  4. long startTime = System.currentTimeMillis();
  5. //执行100万次
  6. for(int i=0;i<1000000;i++){
  7. //判断是否以L开头
  8. if(loveStr.startsWith("L")){
  9. }
  10. }
  11. System.out.println(System.currentTimeMillis() - startTime);
  12. }

结果花费:27ms

StringBuffer和StringBuilder

String对象是不可变对象,因此在需要对字符串进行修改操作时,如字符串连接,替换,String对象总是会生成新的对象,所以其性能相对较差。

例四:

  1. String love = "999玫瑰" + "999电话" + "999晚安" + "999早安";

  1. StringBuilder result = new StringBuilder();
  2. result.append("999玫瑰");
  3. result.append("999电话");
  4. result.append("999晚安");
  5. result.append("999早安");

因为java虚拟机对String拼接进行性能的优化。将多个连接操作的字符串,在编译时合成一个单独的长字符串。所以上面二者效率差不多

虽然二者的性能差不多,但是作者还是建议在代码的实现中尽量地使用StringBuilder或者StringBuffer,来提升程序性能,而不是依靠编译器对程序进行优化。

例五:

  1. // 1
  2. forint i=0;i<10000;i++){
  3. str = str + i;
  4. }
  5. //编译器会把上面代码编译成以下代码,但是每次会新建一个 StringBuilder对象,效率自然就低了
  6. for(int i=0;i<10000;i++){
  7. str = (new StringBuilder(String.valueOf(str))).append(i).toString();
  8. }
  9. // 2
  10. forint i=0;i<10000;i++){
  11. result = result.concat(String.valueOf(i));
  12. }
  13. // 3
  14. StringBuilder sb = new StringBuilder();
  15. for(int i=0;i<10000;i++){
  16. sb.append(i);
  17. }

上面的三个方法中效率(由低到高):
1 < 2 < 3
虽然java虚拟机会String的加法操作进行优化,但是编译器还是不够聪明。

  • StringBuffer和StringBuilder的选择

  • StringBuffer和StringBuilder是一对孪生兄弟。

  • StringBuilder的效率 > StringBuffer的效率

  • StringBuilder是非线程安全的。StringBuffer是线程安全的。