2010-06-04 79 views

回答

290

可变性差异:

String不变,如果你试图改变他们的价值观,另一对象被创建,而StringBufferStringBuilder可变,使他们能够改变自己的价值观。

线程安全的区别:

的差异和StringBuilder之间StringBufferStringBuffer是线程安全的。因此,当应用程序需要在单个线程中运行时,最好使用StringBuilderStringBuilderStringBuffer更有效。

情况:

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

此外,使用字符串进行逻辑操作相当慢,并且根本不建议,因为JVM将字符串转换为字节码中的StringBuffer。大量的开销浪费从String转换为StringBuffer,然后再次返回String。 – 2010-06-04 08:28:00

+2

所以在'Strings'中,当我们改变值时创建另一个对象。旧的对象引用是否被取消,以至于它可能被'GC'垃圾回收,或者甚至是垃圾回收? – 2014-05-09 07:07:18

+0

@PietervanNiekerk你对逻辑运算有什么意义? – emlai 2015-09-29 10:38:38

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的,第E对不同)

关于

StringBufferStringBuilder

前者synchonized,后来是没有的。

所以,如果你调用它几次在一个单独的线程(这是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用于名为“文字”的字符串

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

Ther的没有区别,正好相同代码对两个字符串都执行。

+2

这是一个非常糟糕的例子。使用“Dear”初始化StringBuilder意味着第一个.append()将导致重新分配和复制。完全否定任何您试图通过“正常”串联获得的效率。一个更好的例子是用一个初始大小来创建它,它将保存最终字符串的全部内容。 – 2010-06-04 03:48:30

+1

在分配的右侧使用StringBuilder来执行字符串连接通常不是一个好习惯。正如你所说,任何好的实现都会在幕后使用一个StringBuilder。此外,你的''a“+”b“的例子会被编译成一个单一的字面值'”ab“',但是如果你使用了'StringBuilder',则会导致对append()的两次不必要的调用。 – 2010-06-04 04:18:27

+0

@Mark我不是故意使用'“a”+“b”'而是说什么是*字符串连接*关于我将其更改为显式。你不说的是,为什么不是一个好的做法。这正是现代编译器所做的。 @fuzzy,我同意,特别是当你知道最终字符串的大小是多少时(aprox)。 – OscarRyz 2010-06-04 16:40:18

3

另外,StringBuffer是线程安全的,其中StringBuilder不是。

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

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

查看示例here

+2

+1,简明的答案。 – Dolph 2010-06-04 04:11:58

+2

简洁但不完整,它忽略了使用StringBuilder/Buffer的根本原因,即减少或消除了常规字符串串联行为的重新分配和阵列副本。 – 2010-06-04 04:21:39

+1

“你在处理不可变字符串时使用字符串” - 没有任何意义。字符串的实例是不可变的,所以也许该注释应该是“当由于不变性导致的内存使用不重要时使用字符串”。被接受的答案涵盖了它很好的基础。 – fooMonster 2011-08-03 14:24:41

26

基础:

String是不可变的类,它不能被改变。 StringBuilder是可以附加到,替换的字符或删除,并最终转化为String StringBuffer一个可变类是StringBuilder

原来的同步版本你应该在你只有一个线程访问所有的情况下宁愿StringBuilder你的对象。

的详细信息:

还要注意的是StringBuilder/Buffers不是魔法,他们只是使用数组作为后备对象和数组必须被重新分配时,以往任何时候都得到充分。请确保并创建您的StringBuilder/Buffer对象,这些对象最初足够大,在每次调用.append()时都不需要不断调整其大小。

重新调整大小会变得非常堕落。每次需要扩展时,它基本上都会将后端阵列重新调整为当前大小的2倍。这可能会导致大量RAM分配,并且StringBuilder/Buffer类开始变大时不会使用。

在Java中String x = "A" + "B";在幕后使用StringBuilder。因此,对于简单的情况,宣布您自己没有任何好处。但是,如果您正在构建String较大的对象,比如小于4k,那么声明StringBuilder sb = StringBuilder(4096);比串联或使用只有16个字符的default constructor效率更高。如果您的String将小于10k,则使用构造函数将其初始化为10k以确保安全。但是如果它初始化为10k,那么你写了1个字符超过10k,它将被重新分配并复制到一个20k数组。所以初始化高于低于。

在自动重新调整大小写的情况下,在第17个字符处,支持数组被重新分配并复制到32个字符,在第33个字符处,会再次发生,您将重新分配并将该数组复制为64个字符。你可以看到如何退化到批次的重新分配和副本,这是你真的试图避免使用StringBuilder/Buffer摆在首位。

这是从JDK 6源代码AbstractStringBuilder

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); 
    } 

最佳做法是初始化StringBuilder/Buffer有点大于你认为你会需要,如果你不知道正确的断手String将会有多大,但你可以猜到。比你需要的内存稍多的分配会比大量的重新分配和拷贝更好。

还要小心使用String初始化StringBuilder/Buffer,因为它只会分配String + 16个字符的大小,在大多数情况下,这只会启动您试图避免的简并重新分配和复制循环。以下内容直接来自Java 6源代码。

public StringBuilder(String str) { 
    super(str.length() + 16); 
    append(str); 
    } 

如果你偶然做与StringBuilder/Buffer一个实例,你没有创建和无法控制被称为构造函数结束,还有就是避免退化的方式重新分配并复制行为。请拨打.ensureCapacity(),以确保您的结果String适合。

替代方案:

正如一个音符,如果你正在做真正String建设和操作,还有一个更注重性能的替代称为Ropes

另一种替代方法,是创建一个StringList实行由子类ArrayList<String>,并添加计数器来跟踪的字符上的列表中的每一个.append()和其它突变操作的数量,然后覆盖.toString()创建的确切大小的StringBuilder您需要并遍历列表并构建输出,甚至可以将StringBuilder作为实例变量,并“缓存”.toString()的结果,并且只有在某些更改时才需要重新生成结果。

当构建固定格式化输出时,也请不要忘记String.format(),编译器可以优化它们,因为它们使其更好。

+1

'String x =“A”+“B”;'真的编译成StringBuilder吗?为什么它不能编译为'String x =“AB”;',如果在编译时不知道组件,它应该只使用一个StringBuilder。 – 2010-06-04 03:58:43

+0

它可能会优化字符串常量,我不记得从上次反编译字节码,但我知道如果在那里有任何变量,它肯定会使用StringBuilder实现。您可以下载JDK源代码并找出自己。 “A”+“B”确实是一个人为的例子。 – 2010-06-04 04:06:35

+0

我在想String.format()。我从未真正看到它在项目中使用。通常是StringBuilder。那么,通常它实际上是“A”+“B”+“C”,因为人们懒惰;)我倾向于总是使用StringBuilder,即使它只有两个字符串被连接起来,因为将来可能会追加更多的字符串。我从来没有使用String.format(),主要是因为我从来不记得它引入了什么JDK - 我看到它是JDK1.5,我会用它来支持其他选项。 – jamiebarrow 2010-10-08 10:37:45

3

请注意,如果您使用Java 5或更新版本,则应使用StringBuilder而不是StringBuffer。从API文档:

从发布JDK 5开始,此类已添加了一个旨在供单个线程StringBuilder使用的等效类。 StringBuilder类通常应优先于此类使用,因为它支持所有相同的操作,但速度更快,因为它不执行同步。

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

2

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

2

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

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/

3
 
---------------------------------------------------------------------------------- 
        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 
---------------------------------------------------------------------------------- 
3

String Family

字符串

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

字符串对象是不可变一旦它们被创建,我们不能改变。 (字符串是常数

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

    • Java语言提供对字符串连接符(+)特别支持,以及对其他对象的字符串转换。字符串连接通过StringBuilder(或StringBuffer)类及其append方法实现。

    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的值。

相关问题