Java中的String和StringBuffer有啥区别?

Posted

技术标签:

【中文标题】Java中的String和StringBuffer有啥区别?【英文标题】:What is the difference between String and StringBuffer in Java?Java中的String和StringBuffer有什么区别? 【发布时间】:2011-01-27 05:02:27 【问题描述】:

Java中String和StringBuffer有什么区别?

字符串有最大大小吗?

【问题讨论】:

将来,您可以查看 javadocs 来解决此类问题。 Java 6 javadocs:java.sun.com/javase/6/docs/apiString:java.sun.com/javase/6/docs/api/java/lang/String.htmlStringBuffer:java.sun.com/javase/6/docs/api/java/lang/StringBuffer.html 【参考方案1】:

StringBuffer 用于从多个字符串创建单个字符串,例如当您想在循环中附加部分字符串时。

当您只有一个线程访问 StringBuffer 时,您应该使用 StringBuilder 而不是 StringBuffer,因为 StringBuilder 不同步,因此速度更快。

AFAIK 作为一种语言,Java 中的字符串大小没有上限,但 JVM 可能有上限。

【讨论】:

Strings 有一个 toCharArray() 方法,这必须根据 javadoc 返回字符串的全部内容。 @Simon Nickerson 谢谢,所以上限不超过 Integer.MAX_VALUE。【参考方案2】:

String 用于操作无法更改(只读和不可变)的字符串。

StringBuffer用于表示可以修改的字符。

性能方面,StringBuffer 在执行连接时更快。这是因为当您连接 String 时,每次都在(内部)创建一个新对象,因为 String 是不可变的。

您也可以使用StringBuilder,它类似于StringBuffer,只是它不同步。其中任何一个的最大大小为Integer.MAX_VALUE (231 - 1 = 2,147,483,647) 或最大堆大小除以 2(请参阅How many characters can a Java String have?)。 更多信息here。

【讨论】:

你能给出长度的来源吗? @Thomas java.sun.com/javase/6/docs/api/java/lang/Integer.html#MAX_VALUE 如果你把 2^31 - 1 输入谷歌,你会得到 2,137,483,647 我的意思是为什么字符串不能更长的来源,可能更重要的是,为什么人们可以假设它们可以是 Integer.MAX_VALUE 个字符长而不仅仅是例如2^16。正如我在另一个答案中所指出的,字符串文字只能是 2^16,因为它们需要放入常量池中。 @Thomas,Java 内部用int 表示字符数组中字符的长度。这就是理论长度。 @Thomas:如果我的程序中有一个大小为 2^17 的字符串文字,难道不能在类文件中将它分成两个大小为 2^16 的字符串文字,然后在运行? JVM 规范也可以改变。 Javadocs 就是合同。【参考方案3】:

String 是不可变的,即当它被创建时,它永远不会改变。

StringBuffer(或它的非同步表亲StringBuilder)用于当您需要逐段构造一个字符串而没有一路构造大量小Strings 的性能开销时。

两者的最大长度都是 Integer.MAX_VALUE,因为它们在内部存储为数组,而 Java 数组的长度伪字段只有一个 int

Strings 和StringBuffers 之间的多重串联性能提升非常显着。如果您运行以下测试代码,您将看到差异。在我使用 Java 6 的老式笔记本电脑上,我得到了以下结果:

Concat 与 String 耗时:1781ms Concat 与 StringBuffer 耗时:0ms
public class Concat

    public static String concatWithString()
    
        String t = "Cat";
        for (int i=0; i<10000; i++)
        
            t = t + "Dog";
        
        return t;
    
    public static String concatWithStringBuffer()
    
        StringBuffer sb = new StringBuffer("Cat");
        for (int i=0; i<10000; i++)
        
            sb.append("Dog");
        
        return sb.toString();
    
    public static void main(String[] args)
    
        long start = System.currentTimeMillis();
        concatWithString();
        System.out.println("Concat with String took: " + (System.currentTimeMillis() - start) + "ms");
        start = System.currentTimeMillis();
        concatWithStringBuffer();
        System.out.println("Concat with StringBuffer took: " + (System.currentTimeMillis() - start) + "ms");
    

【讨论】:

【参考方案4】:

String 是一个不可变的字符数组。

StringBuffer 是一个可变字符数组。完成变异后,通常会转换回String

由于两者都是数组,因此两者的最大大小等于整数的最大大小,即 2^31-1(请参阅 JavaDoc,同时查看 String 和 @987654327 的 JavaDoc @)。这是因为数组的.length 参数是原始int。 (见Arrays)。

【讨论】:

你能给出最大长度的来源吗? 数组的长度是一个int,是一个有符号的32位值。 Pindatjuh 引用的值是 Integer.MAX_VALUE,这是正确的。但是类本身并没有强加这个最大长度,因此在某种意义上 Pindatjuh 是不正确的,因为他正在幕后观察实现。没有理由不能将 String 实现为 char[][],然后将其限制为 Long.MAX_VALUE 等等。 @Thomas Lötzer 添加了来源。 在 JLS 中哪里说必须将字符串实现为单个数组?另外:字符串文字不能是 Integer.MAX_VALUE 个字符长,因为它们被放入常量池中。它们只能是 2^16 个字符。如何确定其他字符串也不存在这样的限制? @GregS:但是,String 有一个 toCharArray() 方法,如果 String 的长度超过 Integer.MAX_VALUE 个字符,则无法实现该方法。【参考方案5】:

StringBuffer 或其更年轻、更快的兄弟StringBuilder 是首选,无论何时您要处理大量的字符串连接

string += newString;

或等效

string = string + newString;

因为上述构造每次都会隐式创建 new 字符串,这将是一个巨大的性能下降。 StringBuffer / StringBuilder 最适合与动态可扩展的 List&lt;Character&gt; 进行比较。

【讨论】:

【参考方案6】:

我找到了 Reggie Hutcherso 比较性能 String 与 StringBuffer 的有趣答案 来源:http://www.javaworld.com/javaworld/jw-03-2000/jw-0324-javaperf.html

Java 提供了StringBuffer 和String 类,String 类用于操作不能更改的字符串。简单地说,String 类型的对象是只读且不可变的。 StringBuffer 类用于表示可以修改的字符。

这两个类之间的显着性能差异是 StringBuffer 在执行简单连接时比 String 快。在字符串操作代码中,字符串通常被连接起来。使用 String 类,连接通常按如下方式执行:

 String str = new String ("Stanford  ");
 str += "Lost!!";

如果您要使用 StringBuffer 执行相同的连接,您需要如下所示的代码:

 StringBuffer str = new StringBuffer ("Stanford ");
 str.append("Lost!!");

开发人员通常认为上面的第一个示例效率更高,因为他们认为使用 append 方法进行连接的第二个示例比使用 + 运算符连接两个 String 对象的第一个示例成本更高。

+ 操作符看似无辜,但生成的代码却产生了一些意外。使用 StringBuffer 进行连接实际上可以生成比使用 String 快得多的代码。要发现为什么会这样,我们必须检查两个示例中生成的字节码。使用 String 的示例的字节码如下所示:

0 new #7 <Class java.lang.String>
3 dup 
4 ldc #2 <String "Stanford ">
6 invokespecial #12 <Method java.lang.String(java.lang.String)>
9 astore_1
10 new #8 <Class java.lang.StringBuffer>
13 dup
14 aload_1
15 invokestatic #23 <Method java.lang.String valueOf(java.lang.Object)>
18 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>
21 ldc #1 <String "Lost!!">
23 invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>
26 invokevirtual #22 <Method java.lang.String toString()>
29 astore_1

位置 0 到 9 的字节码针对第一行代码执行,即:

 String str = new String("Stanford ");

然后,执行位置 10 到 29 的字节码进行连接:

 str += "Lost!!";

这里的事情变得有趣了。为连接生成的字节码创建一个 StringBuffer 对象,然后调用它的 append 方法:临时 StringBuffer 对象在位置 10 创建,它的 append 方法在位置 23 处调用。由于 String 类是不可变的,因此必须使用 StringBuffer串联。

对StringBuffer对象进行拼接后,必须将其转回String。这是通过在位置 26 调用 toString 方法来完成的。该方法从临时 StringBuffer 对象创建一个新的 String 对象。这个临时 StringBuffer 对象的创建及其随后转换回 String 对象的成本非常高。

综上所述,上面两行代码导致了三个对象的创建:

    位置 0 的字符串对象 位置 10 的 StringBuffer 对象 位置 26 的字符串对象

现在,让我们看看使用 StringBuffer 为示例生成的字节码:

0 new #8 <Class java.lang.StringBuffer>
3 dup
4 ldc #2 <String "Stanford ">
6 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>
9 astore_1
10 aload_1 
11 ldc #1 <String "Lost!!">
13 invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>
16 pop

位置 0 到 9 的字节码针对第一行代码执行:

 StringBuffer str = new StringBuffer("Stanford ");

然后执行位置 10 到 16 的字节码进行连接:

 str.append("Lost!!");

请注意,与第一个示例中的情况一样,此代码调用 StringBuffer 对象的 append 方法。然而,与第一个示例不同的是,不需要创建临时 StringBuffer 并将其转换为 String 对象。此代码仅在位置 0 处创建一个对象,即 StringBuffer。

总之,StringBuffer 连接明显快于 String 连接。显然,StringBuffers 应该尽可能用于这种类型的操作。如果需要 String 类的功能,请考虑使用 StringBuffer 进行连接,然后执行一次到 String 的转换。

【讨论】:

在 String 与 StringBuffer/Builder 之间进行选择应该不是任何人都应该关心的问题...... JVM 是一个 JIT 环境 -> 为什么 JIT 不能执行合适的优化?跨度> 【参考方案7】:

来自API:

线程安全、可变的字符序列。字符串缓冲区类似于字符串,但可以修改。在任何时间点,它都包含一些特定的字符序列,但是序列的长度和内容可以通过某些方法调用来改变。

【讨论】:

【参考方案8】:

String 是不可变的,这意味着当您对 String 执行操作时,您实际上是在创建一个全新的 String。

StringBuffer 是可变的,您可以附加到它,也可以将其长度重置为 0。

实际上,编译器在字符串连接for performance reasons时似乎使用了StringBuffer。

【讨论】:

【参考方案9】:

String 是一个不可变的类。这意味着一旦你像这样实例化一个字符串的实例:

String str1 = "hello";

内存中的对象无法更改。相反,您必须创建一个新实例,复制旧字符串并附加任何其他内容,如本例所示:

String str1 = "hello";
str1 = str1 + " world!";

真正发生的事情是我们没有更新现有的 str1 对象......我们一起重新分配新内存,复制“hello”数据并附加“world!”到最后,然后将 str1 引用设置为指向这个新内存。所以它看起来更像这样:

String str1 = "hello";
String str2 = str1 + " world!";
str1 = str2;

因此,这种“复制 + 粘贴并在内存中移动内容”的过程可能会非常昂贵,如果重复执行,尤其是递归执行。

当您处于不得不一遍又一遍地做事的情况下,请使用 StringBuilder。它是可变的,并且可以将字符串附加到当前字符串的末尾,因为它由一个 [增长的数组] 返回(如果那是实际的数据结构,则不是 100%,可能是一个列表)。

【讨论】:

【参考方案10】:
String                                          StringBuffer

Immutable                                       Mutable
String s=new String("karthik");                StringBuffer sb=new StringBuffer("karthik")
s.concat("reddy");                             sb.append("reddy");
System.out.println(s);                         System.out.println(sb);
O/P:karthik                                    O/P:karthikreddy

--->once we created a String object            ---->once we created a StringBuffer object
we can't perform any changes in the existing  we can perform any changes in the existing
object.If we are trying to perform any        object.It is nothing but mutablity of 
changes with those changes a new object       of a StrongBuffer object
will be created.It is nothing but Immutability
of a String object

Use String--->If you require immutabilty
Use StringBuffer---->If you require mutable + threadsafety
Use StringBuilder--->If you require mutable + with out threadsafety

String s=new String("karthik");
--->here 2 objects will be created one is heap and the other is in stringconstantpool(scp) and s is always pointing to heap object

String s="karthik"; 
--->In this case only one object will be created in scp and s is always pointing to that object only

【讨论】:

真的吗? String s=new String("karthik"); 会创建两个对象?【参考方案11】:
String is immutable. 

为什么?检查here。

StringBuffer is not. It is thread safe. 

在this之后可以弄清楚何时使用哪些概念和其他概念等更多问题。

希望这会有所帮助。

【讨论】:

【参考方案12】:

虽然我知道这不是主要的区别因素,但我今天注意到 StringBuffer(和 StringBuilder)提供了一些有趣的方法,而 String 没有。

反向() setCharAt()

【讨论】:

【参考方案13】:

在性能方面 StringBuffer 比 String 好得多;因为每当您在 String Object 上应用连接时,都会在每个连接上创建新的 String 对象。

主要规则: String 是不可变的(Non Modifiable),StringBuffer 是可变的(Modifiable)

这是您获得性能差异的程序化实验

public class Test 

  public static int LOOP_ITERATION= 100000;

  public static void stringTest()
    long startTime = System.currentTimeMillis();
    String string = "This";
    for(int i=0;i<LOOP_ITERATION;i++)
        string = string+"Yasir";
    

    long endTime = System.currentTimeMillis();
    System.out.println(endTime - startTime);    
  

  public static void stringBufferTest()
    long startTime = System.currentTimeMillis();
    StringBuffer stringBuffer = new StringBuffer("This");
    for(int i=0;i<LOOP_ITERATION;i++)
        stringBuffer.append("Yasir");
    

    long endTime = System.currentTimeMillis();
    System.out.println(endTime - startTime);
  

  public static void main(String []args)
    stringTest()
    stringBufferTest(); 
  
 

字符串的输出在我的机器上 14800

StringBuffer 的输出在我的机器中 14

【讨论】:

【参考方案14】:

通过在任何追加操作之后打印 String/StringBuffer 对象的哈希码也证明,String 对象每次都在内部使用新值重新创建,而不是使用相同的 String 对象。

public class MutableImmutable 

/**
 * @param args
 */
public static void main(String[] args) 
    System.out.println("String is immutable");
    String s = "test";
    System.out.println(s+"::"+s.hashCode());
    for (int i = 0; i < 10; i++) 
        s += "tre";
        System.out.println(s+"::"+s.hashCode());
    

    System.out.println("String Buffer is mutable");

    StringBuffer strBuf = new StringBuffer("test");
    System.out.println(strBuf+"::"+strBuf.hashCode());
    for (int i = 0; i < 10; i++) 
        strBuf.append("tre");
        System.out.println(strBuf+"::"+strBuf.hashCode());
    

 


输出: 它打印对象值及其哈希码

    String is immutable
    test::3556498
    testtre::-1422435371
    testtretre::-1624680014
    testtretretre::-855723339
    testtretretretre::2071992018
    testtretretretretre::-555654763
    testtretretretretretre::-706970638
    testtretretretretretretre::1157458037
    testtretretretretretretretre::1835043090
    testtretretretretretretretretre::1425065813
    testtretretretretretretretretretre::-1615970766
    String Buffer is mutable
    test::28117098
    testtre::28117098
    testtretre::28117098
    testtretretre::28117098
    testtretretretre::28117098
    testtretretretretre::28117098
    testtretretretretretre::28117098
    testtretretretretretretre::28117098
    testtretretretretretretretre::28117098
    testtretretretretretretretretre::28117098
    testtretretretretretretretretretre::28117098

【讨论】:

【参考方案15】:

区别是

    仅在 String 类中,+ 运算符才被重载。我们可以使用 + 运算符连接两个 String 对象,但对于 StringBuffer 我们不能。

    String 类覆盖了 Object 类的 toString()、equals()、hashCode(),但 StringBuffer 只覆盖了 toString() .

    String s1 = new String("abc");
    String s2 = new String("abc");
    System.out.println(s1.equals(s2));  // output true
    
    StringBuffer sb1 = new StringBuffer("abc");
    StringBuffer sb2 = new StringBuffer("abc");
    System.out.println(sb1.equals(sb2));  // output false
    

    String 类既是 Serializable 又是 Comparable,但 StringBuffer 只是 Serializable。

    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add(sb1);
    set.add(sb2);
    System.out.println(set);  // gives ClassCastException because there is no Comparison mechanism
    

    我们可以使用和不使用new操作符来创建String对象,但是StringBuffer对象只能使用new操作符来创建。

    String 是不可变的,但 StringBuffer 是可变的。 StringBuffer 是同步的,而 String 不是。 StringBuffer 有一个内置的 reverse() 方法,但 String 没有它。

【讨论】:

以上是关于Java中的String和StringBuffer有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Java中的String类与StringBuffer类

Java中的String,StringBuilder,StringBuffer三者的区别

Java中的String,StringBuilder,StringBuffer三者的区别

Java中的String,StringBuilder,StringBuffer三者的区别

Java中的String,StringBuilder,StringBuffer三者的区别

Java中的String,StringBuilder,StringBuffer三者的区别