StringBuilder是java5及以后提供的API,它不是线程安全的,而StringBuffer是java1.4曾经的API,它是线程安全的,所以说StringBuilder的效率更高一些,今天来讲一下在java中字符串连接的几种方法比較。。

写这篇文章主要是关于曾经的一篇文章百度面试题,中因为循环中字符串的连接问题须要优化一下。。

字符串是Java程序中最经常使用的一种数据结构之中的一个。在Java中的String类已经重载的"+"。也就是说,字符串能够直接使用"+"进行连接,如以下代码所看到的:

  1. String s = "abc" + "ddd";

但这样做真的好吗?当然,这个问题不能简单地回答yes or no。要依据详细情况来

定。在Java中提供了一个StringBuilder类(这个类仅仅在J2SE5及以上版本号提供,曾经的版本号使用 StringBuffer类),这个类也能够起到"+"的作用。那么我们应该用哪个呢?

以下让我们先看看例如以下的代码:

  1. package string;
      public class TestSimplePlus
      {
          public static void main(String[] args)
          {
              String s = "abc";
              String ss = "ok" + s + "xyz" + 5;
              System.out.println(ss);
          }
      }

上面的代码将会输出正确的结果。从表面上看,对字符串和整型使用"+"号并没有什么差别,但事实真的如此吗?以下让我们来看看这段代码的本质。

我们首先使用反编译工具(如jdk带的javap、或jad)将TestSimplePlus反编译成Java Byte Code,当中的奥秘就一目了然了。在本文将使用jad来反编译,命令例如以下:

jad -o -a -s d.java TestSimplePlus.class

反编译后的代码例如以下:

  1. package string;  
    
    import java.io.PrintStream;  
    
    public class TestSimplePlus
    {
    public TestSimplePlus()
    {
    // 0 0:aload_0
    // 1 1:invokespecial #8 < Method void Object()>
    // 2 4:return
    } public static void main(String args[])
    {
    String s = "abc";
    // 0 0:ldc1 #16 < String "abc">
    // 1 2:astore_1
    String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
    // 2 3:new #18 < Class StringBuilder>
    // 3 6:dup
    // 4 7:ldc1 #20 < String "ok">
    // 5 9:invokespecial #22 < Method void StringBuilder(String)>
    // 6 12:aload_1
    // 7 13:invokevirtual #25 < Method StringBuilder StringBuilder.append(String)>
    // 8 16:ldc1 #29 < String "xyz">
    // 9 18:invokevirtual #25 < Method StringBuilder StringBuilder.append(String)>
    // 10 21:iconst_5
    // 11 22:invokevirtual #31 < Method StringBuilder StringBuilder.append(int)>
    // 12 25:invokevirtual #34 < Method String StringBuilder.toString()>
    // 13 28:astore_2
    System.out.println(ss);
    // 14 29:getstatic #38 < Field PrintStream System.out>
    // 15 32:aload_2
    // 16 33:invokevirtual #44 < Method void PrintStream.println(String)>
    // 17 36:return
    }
    }

读者可能看到上面的Java字节码感到迷糊,只是大家不必操心。本文的目的并非解说Java Byte Code,因此,并不用了解详细的字节码的含义。

使用jad反编译的优点之中的一个就是能够同一时候生成字节码和源码。这样能够进行对比研究。从上面的代码非常easy看出,尽管在源程序中使用了"+",但在编 译时仍然将"+"转换成StringBuilder。因此,我们能够得出结论,在Java中不管使用何种方式进行字符串连接,实际上都使用的是 StringBuilder类。

那么是不是能够依据这个结论推出使用"+"和StringBuilder类的效果是一样的呢?这个要从两个方面的解释。假设从执行结果来解释,那么"+"和StringBuilder是全然等效的。但假设从执行效率和资源消耗方面看,那它们将存在非常大的差别。

当然,假设连接字符串行表达式非常easy(如上面的顺序结构),那么"+"和StringBuilder类基本是一样的,但假设结构比較复杂,如使用循环来连接字符串,那么产生的Java Byte Code就会有非常大的差别。先让我们看看例如以下的代码:

  1. package string;  
    
     import java.util.*;  
    
     public class TestComplexPlus
    {
    public static void main(String[] args)
    {
    String s = "";
    Random rand = new Random();
    for (int i = 0; i < 10; i++)
    {
    s = s + rand.nextInt(1000) + " ";
    }
    System.out.println(s);
    }
    }

上面的代码返编译后的Java Byte Code例如以下:

  1. package string;  
    
    import java.io.PrintStream;
    import java.util.Random; public class TestComplexPlus
    { public TestComplexPlus()
    {
    // 0 0:aload_0
    // 1 1:invokespecial #8 < Method void Object()>
    // 2 4:return
    } public static void main(String args[])
    {
    String s = "";
    // 0 0:ldc1 #16 < String "">
    // 1 2:astore_1
    Random rand = new Random();
    // 2 3:new #18 < Class Random>
    // 3 6:dup
    // 4 7:invokespecial #20 < Method void Random()>
    // 5 10:astore_2
    for(int i = 0; i < 10; i++)
    //* 6 11:iconst_0
    //* 7 12:istore_3
    //* 8 13:goto 49
    s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
    // 9 16:new #21 < Class StringBuilder>
    // 10 19:dup
    // 11 20:aload_1
    // 12 21:invokestatic #23 < Method String String.valueOf(Object)>
    // 13 24:invokespecial #29 < Method void StringBuilder(String)>
    // 14 27:aload_2
    // 15 28:sipush 1000
    // 16 31:invokevirtual #32 < Method int Random.nextInt(int)>
    // 17 34:invokevirtual #36 < Method StringBuilder StringBuilder.append(int)>
    // 18 37:ldc1 #40 < String " ">
    // 19 39:invokevirtual #42 < Method StringBuilder StringBuilder.append(String)>
    // 20 42:invokevirtual #45 < Method String StringBuilder.toString()>
    // 21 45:astore_1 // 22 46:iinc 3 1
    // 23 49:iload_3
    // 24 50:bipush 10
    // 25 52:icmplt 16
    System.out.println(s);
    // 26 55:getstatic #49 < Field PrintStream System.out>
    // 27 58:aload_1
    // 28 59:invokevirtual #55 < Method void PrintStream.println(String)>
    // 29 62:return
    }
    }

大家能够看到,尽管编译器将"+"转换成了StringBuilder类,但创建StringBuilder对象的位置却在for语句内部。这就意 味着每运行一次循环,就会创建一个StringBuilder对象(对于本例来说,是创建了10个StringBuilder对象),尽管Java有垃圾 回收器,但这个回收器的工作时间是不定的。假设不断产生这种垃圾,那么仍然会占用大量的资源。解决问题的方法就是在程序中直接使用 StringBuilder类来连接字符串,代码例如以下:

  1. package string;  
    
    import java.util.*;  
    
    public class TestStringBuilder
    {
    public static void main(String[] args)
    {
    String s = "";
    Random rand = new Random();
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < 10; i++)
    {
    result.append(rand.nextInt(1000));
    result.append(" ");
    }
    System.out.println(result.toString());
    }
    }

上面代码反编译后的结果例如以下:

  1. package string;  
    
    import java.io.PrintStream;
    import java.util.Random; public class TestStringBuilder
    { public TestStringBuilder()
    {
    // 0 0:aload_0
    // 1 1:invokespecial #8 < Method void Object()>
    // 2 4:return
    } public static void main(String args[])
    {
    String s = "";
    // 0 0:ldc1 #16 < String "">
    // 1 2:astore_1
    Random rand = new Random();
    // 2 3:new #18 < Class Random>
    // 3 6:dup
    // 4 7:invokespecial #20 < Method void Random()>
    // 5 10:astore_2
    StringBuilder result = new StringBuilder();
    // 6 11:new #21 < Class StringBuilder>
    // 7 14:dup
    // 8 15:invokespecial #23 < Method void StringBuilder()>
    // 9 18:astore_3
    for(int i = 0; i < 10; i++)
    //* 10 19:iconst_0
    //* 11 20:istore 4
    //* 12 22:goto 47
    {
    result.append(rand.nextInt(1000));
    // 13 25:aload_3
    // 14 26:aload_2
    // 15 27:sipush 1000
    // 16 30:invokevirtual #24 < Method int Random.nextInt(int)>
    // 17 33:invokevirtual #28 < Method StringBuilder StringBuilder.append(int)>
    // 18 36:pop
    result.append(" ");
    // 19 37:aload_3
    // 20 38:ldc1 #32 < String " ">
    // 21 40:invokevirtual #34 < Method StringBuilder StringBuilder.append(String)>
    // 22 43:pop
    } // 23 44:iinc 4 1
    // 24 47:iload 4
    // 25 49:bipush 10
    // 26 51:icmplt 25
    System.out.println(result.toString());
    // 27 54:getstatic #37 < Field PrintStream System.out>
    // 28 57:aload_3
    // 29 58:invokevirtual #43 < Method String StringBuilder.toString()>
    // 30 61:invokevirtual #47 < Method void PrintStream.println(String)>
    // 31 64:return
    }
    }

从上面的反编译结果能够看出,创建StringBuilder的代码被放在了for语句外。尽管这样处理在源程序中看起来复杂,但却换来了更高的效率,同一时候消耗的资源也更少了。

在使用StringBuilder类时要注意,尽量不要"+"和StringBuilder混着用,否则会创建很多其它的StringBuilder对象,如以下代码所:

  1. for (int i = 0; i <  10; i++)
    {
    result.append(rand.nextInt(1000));
    result.append(" ");
    }
    改成例如以下形式: for (int i = 0; i < 10; i++)
    {
    result.append(rand.nextInt(1000) + " ");
    } 则反编译后的结果例如以下: for(int i = 0; i < 10; i++)
    //* 10 19:iconst_0
    //* 11 20:istore 4
    //* 12 22:goto 65
    {
    result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
    // 13 25:aload_3
    // 14 26:new #21 < Class StringBuilder>
    // 15 29:dup

从上面的代码能够看出,Java编译器将"+"编译成了StringBuilder类,这样for语句每循环一次,又创建了一个StringBuilder对象。

假设将上面的代码在JDK1.4下编译,必须将StringBuilder改为StringBuffer,而JDK1.4将"+"转换为 StringBuffer(由于JDK1.4并没有提供StringBuilder类)。StringBuffer和StringBuilder的功能基 本一样,仅仅是StringBuffer是线程安全的,而StringBuilder不是线程安全的。因此,StringBuilder的效率会更高。

欢迎关注我的微博 
,我的微博会实时更新文章。  交流群:

199326422

StringBuilder和StringBuffer解析(百度面试题优化须要用到的)的更多相关文章

  1. java--String、StringBuilder、StringBuffer的解析和比较?

    一.String的解析 1.String的含义 ①String是不可以被继承的,String类是final类,String类是由char[]数组来存储字符串. ②String是不可变的字符序列,如果存 ...

  2. String,StringBuffer和StringBuilder源码解析[基于JDK6]

    最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西.其实,JDK的源码是越读越有味的.下面总结一下 ...

  3. 解析Java中的String、StringBuilder、StringBuffer类(一)

    引言 String 类及其相关的StringBuilder.StringBuffer 类在 Java 中的使用相当的多,在各个公司的面试中也是必不可少的.因此,在本周,我打算花费一些时间来认真的研读一 ...

  4. 使用StringBuilder或StringBuffer简单优化

    使用StringBuilder或StringBuffer // join(["a", "b", "c"]) -> "a an ...

  5. String、StringBuilder、 StringBuffer 深入分析 源代码解析

    java学习有一段时间了.但学习的东西都是框架等东西,java基础知识有点遗忘.所以重温一下java基础知识.写写文章里面有错的希望大家指正共同进步~~ 一.String 大家常常会说使用" ...

  6. 探秘Java中的String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  7. 探秘Java中String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  8. Java中String、StringBuilder以及StringBuffer

    原文出处: 海子 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String.StringBuilder和StringBuffe ...

  9. 关于 String,StringBuilder,StringBuffer

    关于 String,StringBuilder,StringBuffer 的讨论,已有很多文章:在这里,我希望能刨根问底,更进一步的理解其中的原理. String String 是final类型,不可 ...

随机推荐

  1. [android]Intent跳转新的Activity可以传递数据过去

    两种方式: 一,直接通过Bundle对象来传递: 如果我们想要给“收件人”Activity说点什么的话,那么可以通过下面这封“E-mail”来将我们的消息传递出去 Intent intent=new ...

  2. kafka介绍与搭建(单机版)

    一.kafka介绍 1.1 主要功能 根据官网的介绍,ApacheKafka®是一个分布式流媒体平台,它主要有3种功能: 1:It lets you publish and subscribe to ...

  3. 函数fgets和fputs、fread和fwrite用法小结(转)

    字符串读写函数fgets和fputs: 1.fgets()函数:原型char *fgets(char *s, int n, FILE *stream);从流中读取n-1(n默认1024)个字符之前,如 ...

  4. 提高eclipse使用效率(二)—— 提高Android开发效率的小技巧

    XML文件的代码提示 adt中也有xml文件的代码提示,为了让提示来的更加猛烈,我们还要设置一下 打开eclipse - Window - Preferences,在右边的目录树中切换到XML - X ...

  5. Hadoop案例(十一)MapReduce的API使用

    一学生成绩---增强版 数据信息 computer,huangxiaoming,,,,,,, computer,xuzheng,,,,, computer,huangbo,,,, english,zh ...

  6. USACO 5.5 Picture

    PictureIOI 1998 A number, N (1 <= N < 5000), of rectangular posters, photographs and other pic ...

  7. USACO 4.4 Pollutant Control (网络流求最小割割集)

    Pollutant ControlHal Burch It's your first day in Quality Control at Merry Milk Makers, and already ...

  8. jsp页面中注释 <!-- --> 和<%-- --%> 的区别

    jsp页面中注释 <!-- --> 和<%-- --%> 的区别 原创 2016年09月01日 17:55:44 标签: jsp注释 5605 今天发现一个问题:在jsp代码中 ...

  9. Django实战(21):使用内置的Amin管理用户

    到目前为止,我们开发的所有功能都是匿名访问的,这显然不够安全.通常我们会要求注册的用户通过用户名和密码登录,只有登录后的用户才可以管理产品.套用专业的说法就是:第一步是认证,验证用户是否是他所宣称的那 ...

  10. 2017-2018-1 20179202《Linux内核原理与分析》第九周作业

    进程的切换和系统的一般执行过程 1.知识总结 (1)进程调度的时机: 中断处理过程直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule(). 内核线程是一 ...