String,StringBuffer和StringBuilder

时间:2010-06-04 03:28:55

标签: java string oop stringbuilder

请告诉我比较StringStringBufferStringBuilder的实时情况?

11 个答案:

答案 0 :(得分:357)

可变性差异:

String 不可变,如果您尝试更改其值,则会创建另一个对象,而StringBufferStringBuilder 可变所以他们可以改变自己的价值观。

线程安全差异:

StringBufferStringBuilder之间的区别在于StringBuffer是线程安全的。因此,当应用程序只需要在单个线程中运行时,最好使用StringBuilderStringBuilderStringBuffer效率更高。

<强>情况:

  • 如果您的字符串不会更改,请使用String类,因为String对象是不可变的。
  • 如果您的字符串可以更改(例如:构造字符串中的大量逻辑和操作)并且只能从单个线程访问,则使用StringBuilder就足够了。
  • 如果您的字符串可以更改,并且将从多个线程访问,请使用StringBuffer,因为StringBuffer是同步的,因此您具有线程安全性。

答案 1 :(得分:46)

  • 当不可变结构合适时,使用String;从String获取新的字符序列可能会在CPU时间或内存中带来不可接受的性能损失(获取子字符串是CPU有效的,因为数据未被复制,但这意味着可能会保留更多的数据量分配)。
  • 当您需要创建可变字符序列时,可以使用StringBuilder,通常是将多个字符序列连接在一起。
  • 您在使用StringBuilder的情况下使用StringBuffer,但必须同步对基础字符串的更改(因为多个线程正在读取/修改字符串缓冲区)。

查看示例here

答案 2 :(得分:27)

基础:

String是一个不可变类,无法更改。 StringBuilder是一个可变类,可以追加,替换或删除字符,最终转换为String StringBufferStringBuilder

的原始同步版本

在所有只有一个线程访问对象的情况下,您应该更喜欢StringBuilder

详细信息:

另请注意,StringBuilder/Buffers不是魔术,它们只是使用数组作为后备对象,并且必须在数组填满时重新分配。请确保创建StringBuilder/Buffer最初足够大的.append()个对象,而不必在每次调用StringBuilder/Buffer时不断调整大小。

重新调整大小会变得非常堕落。每次需要扩展时,它基本上将后备阵列的大小重新调整为当前大小的2倍。这会导致大量的RAM被分配,并且在String x = "A" + "B";类开始变大时不会被使用。

在Java StringBuilder幕后使用String。因此,对于简单的情况,声明自己没有任何好处。但是,如果要构建大StringBuilder sb = StringBuilder(4096);个对象(例如小于4k),则声明String比连接或使用仅为16个字符的default constructor更有效。如果你的StringBuilder/Buffer将小于10k,那么使用构造函数将其初始化为10k是安全的。但是如果它初始化为10k然后你写1个字符超过10k,它将被重新分配并复制到20k数组。所以初始化高于低于。

在自动重新大小的情况下,在第17个字符处,后备阵列被重新分配并复制到32个字符,在第33个字符处再次发生这种情况,您可以重新分配并将阵列复制到64个字符。你可以看到这是如何退化为很多的重新分配和副本,这是你真正试图避免首先使用 void expandCapacity(int minimumCapacity) { int newCapacity = (value.length + 1) * 2; if (newCapacity < 0) { newCapacity = Integer.MAX_VALUE; } else if (minimumCapacity > newCapacity) { newCapacity = minimumCapacity; } value = Arrays.copyOf(value, newCapacity); }

这是来自AbstractStringBuilder的JDK 6源代码

StringBuilder/Buffer

最佳做法是将String初始化比您认为自己需要的大一点,如果您不知道StringBuilder/Buffer将会有多大,但您可以猜到。一个比你需要的内存略多的分配将比许多重新分配和副本更好。

还要注意用String初始化public StringBuilder(String str) { super(str.length() + 16); append(str); } ,因为它只会分配String + 16个字符的大小,这在大多数情况下只会启动简并的重新分配和复制周期你试图避免。以下内容直接来自Java 6源代码。

StringBuilder/Buffer

如果你偶然得到一个你没有创建的String实例并且无法控制被调用的构造函数,有一种方法可以避免退化的重新分配和复制行为。使用您想要的尺寸拨打.ensureCapacity(),以确保您的结果String适合。

备选方案:

同样需要注意的是,如果您正在进行 StringList构建和操作,那么有一个更加注重效果的替代方案Ropes

另一种方法是通过子类ArrayList<String>创建.append()实现,并添加计数器来跟踪每个.toString()上的字符数以及列表的其他变异操作,然后重写StringBuilder以创建所需大小的StringBuilder并遍历列表并构建输出,甚至可以使.toString()成为实例变量并“缓存”结果String.format()只有当事情发生变化时才需要重新生成它。

在构建固定格式化输出时也不要忘记{{1}},这可以由编译器进行优化,因为它们会使它更好。

答案 3 :(得分:9)

你的意思是,连接?

真实世界的例子:你想要从许多其他人中创建一个新的字符串

例如发送消息:

字符串

String s = "Dear " + user.name + "<br>" + 
" I saw your profile and got interested in you.<br>" +
" I'm  " + user.age + "yrs. old too"

的StringBuilder

String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) 
          .append(" I saw your profile and got interested in you.<br>") 
          .append(" I'm  " ).append( user.age ).append( "yrs. old too")
          .toString()

String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as  fuzzy lollipop points out.

StringBuffer(语法与StringBuilder完全相同,效果不同)

关于

StringBufferStringBuilder

前者是同步的,后来则没有。

所以,如果你在一个线程中调用它几次(占90%的情况),StringBuilder将更快地运行更多,因为它不会停止查看是否它拥有线程锁。

因此,建议使用StringBuilder(当然,除非你有多个线程同时访问它,这很少见)

编译器可以优化

String连接(使用+运算符)以使用下面的StringBuilder,因此,在长者中不再需要担心在Java的日子里,每个人都认为应该不惜一切代价避免这种情况,因为每个连接都会创建一个新的String对象。现代编译器不再这样做,但是使用StringBuilder代替使用“旧”编译器仍然是一个好习惯。

修改

只是对于谁好奇,这就是编译器为这个类做的事情:

class StringConcatenation {
    int x;
    String literal = "Value is" + x;
    String builder = new StringBuilder().append("Value is").append(x).toString();
}

javap -c StringConcatenation

Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;

java.lang.String literal;

java.lang.String builder;

StringConcatenation();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   12:  ldc #4; //String Value is
   14:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_0
   18:  getfield    #6; //Field x:I
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:  putfield    #9; //Field literal:Ljava/lang/String;
   30:  aload_0
   31:  new #2; //class java/lang/StringBuilder
   34:  dup
   35:  invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   38:  ldc #4; //String Value is
   40:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:  aload_0
   44:  getfield    #6; //Field x:I
   47:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   50:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   53:  putfield    #10; //Field builder:Ljava/lang/String;
   56:  return

}

编号为5 - 27的行用于名为“literal”的字符串

编号为31-53的行用于名为“builder”的字符串

没有区别,两个字符串都执行了相同的代码。

答案 4 :(得分:8)

String Family

<强>字符串

String class表示字符串。 Java程序中的所有字符串文字(例如"abc")都实现为此类的实例。

字符串对象不可变一旦创建,我们就无法改变。 (字符串是常量

  • 如果使用构造函数或方法创建String,则这些字符串将存储在 堆内存 以及SringConstantPool中。但在保存池之前,它会调用 intern() 方法,使用equals方法检查池中相同内容的对象可用性。 如果池中有String-copy,则返回引用。否则,将String对象添加到池中并返回引用。

    • Java语言为字符串连接运算符(+)提供特殊支持,并为其他对象转换为字符串。字符串连接是通过StringBuilder(或StringBuffer)类及其追加方法实现的。

    String heapSCP = new String("Yash");
    heapSCP.concat(".");
    heapSCP = heapSCP + "M";
    heapSCP = heapSCP + 777;
    
    // For Example: String Source Code 
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
    
  • 字符串文字存储在StringConstantPool

    String onlyPool = "Yash";
    

StringBuilderStringBuffer 是可变的字符序列。这意味着可以改变这些对象的价值。 StringBuffer与StringBuilder具有相同的方法,但StringBuffer中的每个方法都是同步的,因此它是线程安全的。

  • 只能使用new运算符创建StringBuffer和StringBuilder数据。因此,它们存储在堆内存中。

  • StringBuilder的实例不适合多线程使用。如果需要这样的同步,则建议使用StringBuffer。

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
    
  • StringBuffer和StringBuilder有一个特殊的方法,如。, replace(int start, int end, String str)reverse()

      

    注意:StringBuffer和SringBuilder是可变的,因为它们提供Appendable Interface的实现。

何时使用哪一个。

  • 如果您不打算每次都更改该值,那么最好使用String Class。作为泛型的一部分,如果您想要排序Comparable<T>或比较值,请转到String Class

    //ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add( threadSafe );
    System.out.println("Set : "+ set);
    
  • 如果你每次去StringBuilder都要修改这个值,这比StringBuffer快。如果多个线程正在修改值,则转到StringBuffer。

答案 5 :(得分:7)

----------------------------------------------------------------------------------
                  String                    StringBuffer         StringBuilder
----------------------------------------------------------------------------------                 
Storage Area | Constant String Pool         Heap                   Heap 
Modifiable   |  No (immutable)              Yes( mutable )         Yes( mutable )
Thread Safe  |      Yes                     Yes                     No
 Performance |     Fast                 Very slow                  Fast
----------------------------------------------------------------------------------

答案 6 :(得分:4)

此外,StringBuffer是线程安全的,StringBuilder不是。

因此,在实时情况下,当不同的线程访问它时,StringBuilder可能会产生不确定的结果。

答案 7 :(得分:3)

请注意,如果您使用的是Java 5或更高版本,则应使用StringBuilder代替StringBuffer。从API文档:

  

从JDK 5发行版开始,这个类已经补充了一个设计用于单个线程StringBuilder的等效类。通常应优先使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步。

实际上,您几乎不会同时从多个线程中使用它,因此StringBuffer所做的同步几乎总是不必要的开销。

答案 8 :(得分:3)

就个人而言,我认为StringBuffer没有真正的世界用途。我什么时候想通过操纵字符序列在多个线程之间进行通信?这根本听起来不太有用,但也许我还没有看到光:)

答案 9 :(得分:3)

String与其他两个类的区别在于String是不可变的,另外两个是可变类。

但为什么我们有两个类用于同一目的?

原因是StringBuffer是线程安全的而StringBuilder不是。 StringBuilderStringBuffer Api上的新类,它已在JDK5中引入,如果您在单线程环境中工作,则始终建议Faster

有关完整的详细信息,请阅读http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

答案 10 :(得分:2)

在java中, String 是不可变的。不可变是指一旦创建了String,我们就无法改变它的值。 StringBuffer 是可变的。创建StringBuffer对象后,我们只需将内容附加到object的值,而不是创建新对象。 StringBuilder 类似于StringBuffer,但它不是线程安全的。 StingBuilder的方法不同步,但与其他Strings相比,Stringbuilder运行速度最快。 您可以通过实施它们来了解String, StringBuilder and StringBuffer之间的区别。