给定下面的2个toString()实现,哪个是首选的:

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

or

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?


当前回答

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

我有一个包含10k条记录的JDBC结果集(是的,我需要将它们全部放在一个批处理中)。在我的Java 1.8机器上使用+运算符大约需要5分钟。对于相同的查询,使用stringBuilder.append("")只需不到一秒。

所以差异是巨大的。在循环中,StringBuilder要快得多。

其他回答

我喜欢:

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

...因为它简短易读。

我不会为速度而优化它,除非您在重复次数非常高的循环中使用它,并测量了性能差异。

我同意,如果必须输出大量参数,这个表单可能会令人困惑(就像其中一个评论所说的那样)。在这种情况下,我将切换到更可读的形式(可能使用apache-commons的ToStringBuilder -取自matt b的答案),并再次忽略性能。

我可以指出,如果你要迭代一个集合并使用StringBuilder,你可能想要检查Apache Commons Lang和StringUtils.join()(在不同的口味)?

不管性能如何,它可以让你省去无数次创建StringBuilders和for循环的麻烦。

使用'+'的字符串连接的性能更昂贵,因为它必须创建一个全新的字符串副本,因为字符串在java中是不可变的。如果连接非常频繁,例如:在循环中,这将发挥特别的作用。 以下是我的想法,当我试图做这样的事情:

一般规则:

在单个字符串赋值中,使用字符串连接是很好的。 如果您正在循环构建一个大的字符数据块,请使用StringBuffer。 在String上使用+=总是比使用StringBuffer效率低,所以它应该敲响警钟——但在某些情况下,所获得的优化与可读性问题相比微不足道,所以使用你的常识。

这里有一篇关于这个话题的Jon Skeet博客。

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

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

Java:

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

ByteCode:(用于循环摘录)

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:(循环摘录)

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

不过,两者还是有一些明显的区别。在第一种情况下,使用了+,为每次for循环迭代创建新的StringBuilder,并通过执行toString()调用存储生成的结果(29到41)。所以当你在for循环中使用+运算符时,你会生成你真正不需要的中间字符串。

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

我有一个包含10k条记录的JDBC结果集(是的,我需要将它们全部放在一个批处理中)。在我的Java 1.8机器上使用+运算符大约需要5分钟。对于相同的查询,使用stringBuilder.append("")只需不到一秒。

所以差异是巨大的。在循环中,StringBuilder要快得多。