Java中toString()中的StringBuilder vs String串联


925

鉴于以下2种toString()实现方式,首选其中一种:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

要么

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

更重要的是,假设我们只有3个属性,那么这可能没有什么不同,但是您将在什么时候从+concat 切换到 StringBuilder


40
在什么时候切换到StringBuilder?当它影响内存或性能时。或何时可能。如果您实际上只对几个字符串执行一次,则无需担心。但是,如果要一遍又一遍地进行操作,则在使用StringBuilder时应该会看到可测量的差异。
ewall

参数100的平均值是多少?
Asif Mushtaq,

2
@UnKnown 100是StringBuilder的初始大小
非续集,

@nonsequitor那么最大字符将是100?
Asif Mushtaq

10
@Unknown不只是初始大小,如果您知道要处理的字符串的大致大小,则可以告诉StringBuilder要分配多少大小,否则,如果空间不足,则必须通过创建一个大小来将大小加倍新char[]阵列然后复制数据-这非常昂贵。您可以通过提供大小来作弊,然后就不需要创建数组了-因此,如果您认为字符串的长度约为100个字符,则可以将StringBuilder设置为该大小,而不必在内部进行扩展。
非续集'16

Answers:


964

版本1是更可取的,因为它更短,并且编译器实际上会将其转换为版本2-完全没有性能差异。

更重要的是,鉴于我们只有3个属性,这可能没有什么不同,但是在什么时候从concat切换到builder?

在您串联连接的那一刻-通常是编译器无法自行替代时StringBuilder


19
没错,但是语言参考也指出这是可选的。实际上,我只是用JRE 1.6.0_15做了一个简单的测试,并且在反编译类中没有看到任何编译器优化。
bruno conde

37
我只是从问题中尝试了代码(在JDK 1.6.0_16上编译),并发现了预期的优化。我很确定所有现代编译器都可以做到。
Michael Borgwardt

22
你是对的。查看字节码,我可以清楚地看到StringBuilder优化。我正在使用反编译器,并且在某种程度上,它正在转换回concat。+1
bruno conde

80
不是要打败老马,而是规范中的用词是:可能To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.存在关键词。鉴于这是官方可选的(尽管很可能已实施),我们是否应该保护自己?
卢卡斯

93
@卢卡斯:不,我们不应该。如果编译器决定不执行该优化,那是因为这样做不值得。在99%的情况下,编译器会更好地知道哪种优化是值得的,因此凭经验法则,开发人员不应干预。当然,您的情况可能会降到其他1%,但这只能通过(仔细)基准测试来检查。
sleske 2012年

255

关键是要在一个地方编写单个串联还是随着时间的推移积累它。

对于您给出的示例,明确使用StringBuilder没有意义。(在第一种情况下,请查看编译后的代码。)

但是,如果要在循环中生成字符串,请使用StringBuilder。

为了澄清起见,假设hugeArray包含数千个字符串,则代码如下:

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

与以下内容相比非常浪费时间和内存:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();

3
是的,StringBuilder不需要一遍又一遍地重新创建String对象。
Olga 2013年

124
该死的,我用那两个函数测试了我正在工作的一个大字符串。6.51min vs 11secs
user1722791 2013年

1
顺便说一下,您也可以使用result += s;它(在第一个示例中)
RAnders00

1
该语句将创建多少个对象?"{a:"+ a + ", b:" + b + ", c: " + c +"}";
阿西夫·穆斯塔克

1
怎么样:String str =(a == null)?null:a'+(b == null)吗?null:b'+(c == null)吗?c:c'+ ...; ?这会阻止优化的进行吗?
amitfr '16

75

我更喜欢:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

...因为它简短易读。

我会 除非您在循环计数很高的循环中使用它并且测量出性能差异,否则不会优化速度。

我同意,如果您必须输出很多参数,则这种形式可能会造成混淆(就像其中一项评论所述)。在这种情况下,我将切换为更具可读性的形式(也许使用apache-commons的ToStringBuilder-取自matt b的答案)并再次忽略性能。


64
它实际上更长,包含更多符号,并且变量文本乱序。
Tom Hawtin-抢险活动

4
因此,您会说它比其他方法之一的可读性差吗?
tangens

3
我更喜欢编写此代码,因为添加更多变量更容易,但是我不确定它是否更具可读性-尤其是随着参数数量的增加。当您需要在不同时间添加位时,它有时也不起作用。
Alex Feinman

78
似乎更难阅读(对我而言)。现在,我必须在{...}和参数之间来回扫描。
郭富城2009年

10
我更喜欢这种形式,因为如果其中一个参数是null
rds

74

在大多数情况下,您不会看到这两种方法之间的实际差异,但是很容易构造这样的最坏情况:

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

输出为:

slow elapsed 11741 ms
fast elapsed 7 ms

问题在于,将+ =追加到字符串后会重新构造一个新字符串,因此它的成本与字符串长度(两者之和)成线性关系。

所以-对您的问题:

第二种方法会更快,但可读性较差且难以维护。正如我所说,在您的特定情况下,您可能看不到区别。


不要忘记.concat()。我估计经过的时间在10到18毫秒之间,这样在使用短字符串(如原始帖子示例)时可以忽略不计。
Droo

9
当你正确的时候 +=,原始示例是的序列+,编译器将其转换为单个string.concat调用。您的结果不适用。
布林迪

1
@Blindy&Droo:-你们都是正确的。在这种情况下使用.concate是最好的解决方法,因为每次循环例程执行时+ =都会创建新对象。
perilbrain

3
你知道他的toString()不是循环调用的吗?
奥姆里·雅丹

我已经尝试过这个例子来测试速度。所以我的结果是:慢速经过29672毫秒;快速经过15毫秒。因此答案很明显。但是,如果要进行100次迭代-时间是相同的-0毫秒。如果进行500次迭代-16毫秒和0毫秒。等等。
Ernestas Gruodis

28

我也和老板就是否使用append或+发生了冲突。因为他们正在使用Append(每次创建新对象时,我仍然无法弄清他们的说法)。所以我想做一些研发工作。虽然我喜欢Michael Borgwardt的解释,但只是想展示一个解释,如果将来有人真的需要知道。

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

以上类的分解出来

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

从上面的两个代码中可以看出Michael是正确的,在每种情况下仅创建一个SB对象。


27

从Java 1.5开始,使用“ +”和StringBuilder.append()进行简单的一行连接即可生成完全相同的字节码。

因此,出于代码可读性考虑,请使用“ +”。

2个例外:

  • 多线程环境:StringBuffer
  • 循环中的串联:StringBuilder / StringBuffer

22

使用最新版本的Java(1.8),disassembly(javap -c)显示了编译器引入的优化。+以及sb.append()将产生非常相似的代码。但是,如果我们使用的话,检查行为是值得的+在for循环。

在for循环中使用+添加字符串

Java:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode :( for循环摘录)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

使用stringbuilder.append添加字符串

Java:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe :( for循环摘录)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

虽然有一些明显的不同。在第一种情况下,+使用时,StringBuilder将为每个for循环迭代创建new ,并通过toString()调用(29到41)存储生成的结果。因此,您将生成+for循环使用operator时实际上不需要的中间字符串。


1
这是Oracle JDK还是OpenJDK?
Christophe Roussy

12

在Java 9中,版本1应该更快,因为它可以转换为invokedynamic调用。可以在JEP-280中找到更多详细信息:

这个想法是用对java.lang.invoke.StringConcatFactory的简单invokedynamic调用来替换整个StringBuilder追加操作,该调用将接受需要连接的值。


9

出于性能原因,不建议使用+=String串联)。原因是:Java String是不可变的,每次执行新的串联操作时String都会创建一个新的字符串(新字符串的指纹与String池中已有的旧指纹的指纹不同))。创建新的字符串给GC带来压力,并减慢了程序的速度:对象创建非常昂贵。

下面的代码应该使它同时更加实用和清晰。

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

运行结果报告如下。

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

不考虑1个串联的结果(JIT尚未完成其工作),即使对于10个串联,其性能损失也很重要。对于成千上万个串联,差异是巨大的。

从这个非常快速的实验中吸取的教训(可以轻松地用上面的代码重现):+=即使在需要几个连接的非常基本的情况下,也不要使用来将字符串连接在一起(如上所述,创建新的字符串无论如何都是昂贵的,并且给字符串带来了压力。 GC)。


9

请参阅以下示例:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

输出:

java字节码版本:8
java.version:1.8.0_144
[str1.concat(str2)]
10000个串联的平均时间:0.096 ms
10000个串联的平均时间:0.185 ms平均
10000个串联的平均时间:0.327 ms平均10000个串联的
平均时间10000个连接:10000个连接的
平均时间为0.501 ms 平均时间
创建的字符串长度:1950000 in 17745 ms
[str1 + = str2]
10000个连接的平均时间:0.21 ms平均
10000个连接的平均时间:0.652 ms
平均时间10000个串联: 平均1.129毫秒10000个串联的平均时间:2.302毫秒平均
平均平均时间:1.727毫秒平均

创建的长度为1950000的字符串在60279 ms中
[str1.append(str2)]
10000个串联的
平均时间:0.002 ms
10000个串联的平均时间:0.002 ms 10000个串联的平均时间:0.002 ms
10000个串联的平均时间: 0.002毫秒AVG
平均时间10000个级联:0.002毫秒AVG
创建长度的字符串:1950000在100毫秒

随着字符串长度的增加,连接时间也会增加。
那是StringBuilder绝对需要的地方。
如您所见,串联:UUID.randomUUID()+"---"并没有真正影响时间。

PS:我不认为何时在Java中使用StringBuilder确实是重复的。
这个问题讨论的是toString(),大多数时候哪个时间不执行大型字符串的连接。


2019更新

由于java8时间,事情已经改变了一点。看来now(java13)的串联时间+=实际上与相同str.concat()。但是,StringBuilder串联时间仍然是恒定的。(上面的原始帖子经过了稍微的编辑,以添加更多详细的输出)

java字节码版本:13
java.version:13.0.1
[str1.concat(str2)]
10000个连接的平均时间:0.047 ms
10000个连接的平均时间:0.1 ms avg
10000个连接的平均时间:0.17 ms avg的
平均时间10000个连接:10000个连接的
平均时间为0.255 ms 平均时间
创建的字符串长度:1950000 in 9147 ms
[str1 + = str2]
10000个连接的平均时间:0.037 ms平均
10000个连接的平均时间:0.097 ms
平均时间10000次串联: 平均0.249毫秒10000次串联的平均时间:0.326毫秒平均
平均平均时间:0.298毫秒平均

创建的字符串长度:1950000 in 10191 ms
[str1.append(str2)]
10000个串联的
平均时间:0.001 ms
10000个串联的平均时间:0.001 ms 10000个串联的平均时间:0.001 ms
10000个串联的平均时间: 0.001毫秒AVG
平均时间10000个级联:0.001毫秒AVG
创建长度的字符串:1950000在43毫秒

值得注意的是,bytecode:8/java.version:13与之相比,组合具有良好的性能优势bytecode:8/java.version:8


这应该是一个可以接受的答案..它取决于决定选择concat还是StringBuilder的String Stream的大小
user1428716

@ user1428716仅供参考:答案已更新,但结果java13有所不同。但我认为主要结论仍然相同。
马里诺斯(Marinos)

7

Apache Commons-Lang具有一个ToStringBuilder类,该类非常易于使用。在处理附加逻辑以及格式化toString的外观方面,它都做得很好。

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

将返回类似的输出com.blah.YourClass@abc1321f[a=whatever, b=foo]

或者使用链接更简洁的形式:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

或者,如果您想使用反射来包含类的每个字段,请执行以下操作:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

您还可以根据需要自定义ToString的样式。


4

使toString方法尽可能地可读!

在我的书中,唯一的例外是您是否可以证明,我认为它消耗显著资源:)(是的,这个手段剖析)

还请注意,与早期Java版本中使用的手写“ StringBuffer”方法相比,Java 5编译器生成的代码速度更快。如果您使用“ +”,则此功能和将来的增强功能是免费提供的。


3

对于当前的编译器是否仍然需要使用StringBuilder,似乎存在一些争论。所以我想我会给我2美分的经验。

我有一个1 JDBC万条记录的结果集(是的,我需要全部一批。)使用+运算符在上需要大约5分钟Java 1.8stringBuilder.append("")同一查询的使用时间不到一秒钟。

因此差异很大。循环StringBuilder内要快得多。


2
我认为辩论是关于在循环之外使用它。我认为需要达成共识,您需要在循环中使用它。
乔丹

2

在性能上使用'+'进行字符串连接比较昂贵,因为它必须制作一个全新的String副本,因为String在Java中是不可变的。如果连接非常频繁,例如在循环内,则此功能起特殊作用。以下是我尝试执行此操作时IDEA的建议:

在此处输入图片说明

通用规则:

  • 在单个字符串分配中,可以使用String串联。
  • 如果要循环构建大量字符数据,请使用StringBuffer。
  • 在String上使用+ =总是比使用StringBuffer效率低,因此它应该发出警告-但在某些情况下,与可读性问题相比,所获得的优化将是微不足道的,因此请使用常识。

这是有关该主题的一个不错的Jon Skeet博客


2
除非绝对需要来自多个线程的同步访问,否则永远不要使用StringBuffer。否则,请选择StringBuilder,因为它不是同步的,因此开销较小。
埃尔基·德·卢尼


1

这是我在Java8中检查的内容

  • 使用字符串串联
  • 使用StringBuilder

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

如果要对大量字符串使用字符串连接,那真是一场噩梦。

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

-1

我认为我们应该使用StringBuilder追加方法。原因是:

  1. String串联每次都会创建一个新的字符串对象(因为String是不可变的对象),因此它将创建3个对象。

  2. 使用String builder,将只创建一个对象[StringBuilder是可变的],并将其他字符串附加到该对象。


为什么这个答案被否决?docs.oracle.com/javase/8/docs/api/java/util/stream/…-可变约简
user1428716

-4

对于像这样的简单字符串,我更喜欢使用

"string".concat("string").concat("string");

按顺序,我会说构造字符串的首选方法是使用StringBuilder,String#concat(),然后使用重载+运算符。当处理大型字符串时,如使用+运算符会大大降低性能(随着String大小的增加呈指数下降),StringBuilder会显着提高性能。使用.concat()的一个问题是它可能引发NullPointerExceptions。


9
使用concat()的性能可能会比'+'更差,因为JLS允许将'+'转换为StringBuilder,并且很可能所有JVM都会这样做或使用更有效的替代方法-事实并非如此。 concat必须在示例中创建并丢弃至少一个完整的中间字符串。
劳伦斯·多尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.