Java 字符串、StringBuffer和StringBuilder之间的性能和简单性权衡

Java 字符串、StringBuffer和StringBuilder之间的性能和简单性权衡,java,architecture,string,Java,Architecture,String,您是否想过Java编程语言中这种变化的含义 String类被认为是一个不可变的类(这个决定是经过深思熟虑的)。但是字符串连接非常慢,我自己也做过基准测试。于是,字符串缓冲区诞生了。非常棒的课程,同步,速度非常快。但有些人对某些同步块的性能成本不满意,于是引入了StringBuilder 但是,当使用字符串连接不太多的对象时,类的不变性使它成为实现线程安全的一种非常自然的方式。当我们想要管理多个字符串时,我可以理解StringBuffer的用法。但是,这是我的第一个问题: 例如,如果您有10个或更

您是否想过Java编程语言中这种变化的含义

String类被认为是一个不可变的类(这个决定是经过深思熟虑的)。但是字符串连接非常慢,我自己也做过基准测试。于是,字符串缓冲区诞生了。非常棒的课程,同步,速度非常快。但有些人对某些同步块的性能成本不满意,于是引入了StringBuilder

但是,当使用字符串连接不太多的对象时,类的不变性使它成为实现线程安全的一种非常自然的方式。当我们想要管理多个字符串时,我可以理解StringBuffer的用法。但是,这是我的第一个问题:

  • 例如,如果您有10个或更少的字符串要追加,您会用简单性换取执行时间的几毫秒吗?

    我也对StringBuilder进行了基准测试。它比StringBuffer更高效(仅提高10%)。但是,如果在单线程程序中使用的是StringBuilder,那么如果有时要将设计更改为使用多个线程,会发生什么情况?您必须更改StringBuilder的每个实例,如果您忘记了一个实例,您将产生一些奇怪的效果(考虑到可能出现的竞争条件)

  • 在这种情况下,您会用性能换取调试时间吗?

  • 好了,就这些。除了这个简单的问题(StringBuffer比“+”和线程安全更有效,StringBuilder比StringBuffer更快,但没有线程安全),我想知道何时使用它们


    (重要提示:我知道它们之间的区别;这是一个与平台架构和一些设计决策相关的问题。)

    StringBuffer在Java 1.0中;这不是对缓慢性或不变性的任何反应。它也不会比字符串连接更快或更好;事实上,Java编译器编译

    String s1 = s2 + s3;
    
    变成

    String s1 = new StringBuilder(s2).append(s3).toString();
    
    如果你不相信我的话,你可以自己用反汇编程序(比如javap-c)试试

    “StringBuffer比串联快”指的是重复串联。在这种情况下,显式地创建yoir自己的StringBuffer并反复使用它比让编译器创建其中的许多要更好

    正如您所说,Java 5中引入StringBuilder是出于性能原因。之所以有意义,是因为StringBuffer/Builder实际上从未在创建它们的方法之外共享过:它们99%的使用率与上述类似,它们是在上面创建的,用于将几个字符串附加在一起,然后被丢弃。

    仅是对“StringBuffers和线程”的评论备注:即使在多线程程序中,要跨多个线程构建字符串是非常罕见的。通常,每个线程都会有一些数据集并从中创建一个字符串,通常是通过将多个字符串连接在一起。然后,他们将把
    StringBuilder
    转换为一个字符串,该字符串可以在线程之间安全地共享

    我认为我从未见过由于线程之间共享
    StringBuilder
    而导致的bug

    我个人希望
    StringBuffer
    不存在-它处于Java的“让我们同步一切”阶段,导致
    Vector
    Hashtable
    几乎被Java2中未同步的
    ArrayList
    HashMap
    类淘汰。只是花了一点时间,非同步等价的
    StringBuffer
    才到达

    所以基本上:

    • 当您不想执行操作,并且希望确保其他操作不会执行时,请使用字符串
    • 使用
      StringBuilder
      执行操作,通常在短时间内执行
    • 避免使用
      StringBuffer
      ,除非您真的、真的需要它——正如我所说,我不记得曾经见过这样的情况:当两者都可用时,我会使用
      StringBuffer
      而不是
      StringBuilder

    如今,StringBuffer和Builder都有点无用(从性能角度来看)。 我解释原因:

    StringBuilder应该比StringBuffer快,但是任何正常的JVM都可以优化同步。因此,当它被引入时,它是一个巨大的失误(和小的成功)

    StringBuffer用于在创建字符串时不复制字符[](在非共享变量中);然而,这是问题的主要来源,包括泄漏小字符串的大字符[]。在1.5中,他们决定char[]的副本每次都必须出现,这实际上使StringBuffer变得无用(同步是为了确保线程游戏不会欺骗字符串)。这节省了内存,但最终有助于GC(除了明显减少的内存占用之外),通常char[]是消耗内存的前三位对象

    concat过去是,现在仍然是连接2个字符串的最快方法(并且只有2个…或者可能是3个)。请记住,它不会执行char[]的额外副本

    回到无用的部分,现在任何第三方代码都可以实现与StringBuilder相同的性能。即使在java1.1中,我也曾经有一个类名AsycnStringBuffer,它的作用与StringBuilder现在的作用完全相同,但它仍然比StringBuilder分配更大的char[]。StrinBuffer/StringBuilder都针对小字符串进行了优化。默认情况下,您可以看到c-tor

      StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
        }
    
    因此,如果第二个字符串的长度超过16个字符,它将获得基础字符串的另一个副本 字符[]。很不酷

    这可能是在32位操作系统上尝试将StringBuilder/Buffer和char[]安装到同一缓存线(在x86上)的副作用。。。但我不确定

    至于调试时间等的评论,根据你的判断,我个人不记得e
    package java.lang;
    
    public class FastConcat {
    
        public static String concat(String s1, String s2){
            s1=String.valueOf(s1);//null checks
            s2=String.valueOf(s2);
    
            return s1.concat(s2);
        }
    
        public static String concat(String s1, String s2, String s3){
            s1=String.valueOf(s1);//null checks
            s2=String.valueOf(s2);
            s3=String.valueOf(s3);
            int len = s1.length()+s2.length()+s3.length();
            char[] c = new char[len];
            int idx=0;
            idx = copy(s1, c, idx);
            idx = copy(s2, c, idx);
            idx = copy(s3, c, idx);
            return newString(c);
        }
        public static String concat(String s1, String s2, String s3, String s4){
            s1=String.valueOf(s1);//null checks
            s2=String.valueOf(s2);
            s3=String.valueOf(s3);
            s4=String.valueOf(s4);
    
            int len = s1.length()+s2.length()+s3.length()+s4.length();
            char[] c = new char[len];
            int idx=0;
            idx = copy(s1, c, idx);
            idx = copy(s2, c, idx);
            idx = copy(s3, c, idx);
            idx = copy(s4, c, idx);
            return newString(c);
    
        }
        private static int copy(String s, char[] c, int idx){
            s.getChars(c, idx);
            return idx+s.length();
    
        }
        private static String newString(char[] c){
            return new String(0, c.length, c);
            //return String.copyValueOf(c);//if not in java.lang
        }
    }
    
       private int testcount = 1000; 
       private int elementCount = 50000;
    
       public void testStringBuilder() {
    
        long total = 0;
        int counter = 0;
        while (counter++ < testcount) {
            total += doStringBuilder();
        }
        float f = (total/testcount)/1000;
        System.out.printf("StringBuilder build & output duration= %f µs%n%n", f); 
    }
    
    private long doStringBuilder(){
        long start = System.nanoTime();
        StringBuilder buffer = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        buffer.append("<root>");
          for (int i =0; i < elementCount; i++) {
              buffer.append("<data/>");
          }
          buffer.append("</root>");
         //System.out.println(buffer.toString());
          output = buffer.toString();
          long end = System.nanoTime();
         return end - start;
    }
    
    
    public void testStringBuffer(){
        long total = 0;
        int counter = 0;
        while (counter++ < testcount) {
            total += doStringBuffer();
        }
        float f = (total/testcount)/1000;
    
        System.out.printf("StringBuffer build & output duration= %f µs%n%n", f); 
    }
    
    private long doStringBuffer(){
        long start = System.nanoTime();
        StringBuffer buffer = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        buffer.append("<root>");
          for (int i =0; i < elementCount; i++) {
              buffer.append("<data/>");
          }
          buffer.append("</root>");
         //System.out.println(buffer.toString());
          output = buffer.toString();
    
          long end = System.nanoTime();
          return end - start;
    }
    
    On OSX machine: StringBuilder build & output duration= 1047.000000 µs StringBuffer build & output duration= 1844.000000 µs On Win7 machine: StringBuilder build & output duration= 1869.000000 µs StringBuffer build & output duration= 2122.000000 µs
    StringBuffer build & output duration= 4282,000000 µs
    StringBuilder build & output duration= 4226,000000 µs
    StringBuffer build & output duration= 4439,000000 µs
    StringBuilder build & output duration= 3961,000000 µs
    StringBuffer build & output duration= 4801,000000 µs
    StringBuilder build & output duration= 4210,000000 µs