鉴于下面的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
?
18 回答
关键是你是在一个地方写一个连接还是随着时间累积它 .
对于您给出的示例,明确使用StringBuilder没有意义 . (查看第一个案例的编译代码 . )
但是如果你要构建一个字符串,例如在循环内部,使用StringBuilder .
为了澄清,假设hugeArray包含数千个字符串,代码如下:
与以下相比,是非常浪费时间和内存的:
性能明智使用''进行字符串连接是比较昂贵的,因为它必须创建一个全新的String副本,因为字符串在java中是不可变的 . 如果连接非常频繁,例如:在循环内部,这会起特殊作用 . 以下是我的IDEA在尝试做这样的事情时的建议:
通用规则:
在单个字符串赋值中,使用字符串连接很好 .
如果要循环构建大块字符数据,请转到StringBuffer .
在String上使用=总是比使用StringBuffer效率低,所以它应该响铃警告 - 但在某些情况下,与可读性问题相比,获得的优化可以忽略不计,因此请使用常识 .
这是围绕这个主题的nice Jon Skeet blog .
使用最新版本的Java(1.8),反汇编(
javap -c
)显示了编译器引入的优化 .+
以及sb.append()
将生成非常相似的代码 . 但是,如果我们在for循环中使用+
,那么检查行为是值得的 .Adding strings using + in a for loop
Java的:
ByteCode :(
for
循环摘录)Adding strings using stringbuilder.append
Java的:
ByteCdoe :(
for
循环摘录)虽然有一点 glaring difference . 在第一种情况下,在使用
+
的情况下,为每个for循环迭代创建新的StringBuilder
,并通过执行toString()
调用(29到41)来存储生成的结果 . 因此,在for
循环中使用+
运算符时,您正在生成您真正不需要的中间字符串 .请参阅以下示例:
输出:
As the string length increases, so does the concatenation time.
这就是
StringBuilder
绝对需要的地方 .如你所见,串联:
UUID.randomUUID()+"---"
,并不会真正影响时间 .P.S.: 我认为When to use StringBuilder in Java确实与此重复 .
这个问题谈到
toString()
,其中大多数时候都没有执行大字符串的连接 .我更喜欢:
...因为它简短易读 .
我会 not 优化这个速度,除非你在具有非常高的重复次数的循环中使用它 and 测量了性能差异 .
我同意,如果你必须输出很多参数,这个表格会让人感到困惑(就像其中一条评论所说) . 在这种情况下,我会切换到一个更易读的形式(也许使用ToStringBuilder的apache-commons - 取自matt b的答案)并再次忽略性能 .
Apache Commons-Lang有一个ToStringBuilder类,非常容易使用 . 它既可以处理附加逻辑,也可以格式化你想要的toString外观 .
将返回看起来像
com.blah.YourClass@abc1321f[a=whatever, b=foo]
的输出 .或者使用链接以更精简的形式:
或者,如果您想使用反射来包含该类的每个字段:
如果需要,您还可以自定义ToString的样式 .
对于当前的编译器是否仍然需要使用StringBuilder似乎存在争议 . 所以我想我会给我2美分的经验 .
我有一个10k记录的
JDBC
结果集(是的,我需要一批中的所有记录 . )使用Java 1.8
在我的机器上使用运算符大约需要5分钟 . 对于同一查询,使用stringBuilder.append("")
只需不到一秒钟 .所以差异很大 . 循环内部
StringBuilder
要快得多 .我可以指出,如果你要迭代一个集合并使用StringBuilder,你可能想查看Apache Commons Lang和StringUtils.join()(不同的风格)?
无论性能如何,它都将为您节省必须创建StringBuilders和for循环,这似乎是第百万次 .
我比较了四种不同的方法来比较性能 . 我完全不知道gc会发生什么,但对我来说重要的是时间 . 编译器是这里的重要因素 . 我在window8.1平台下使用了jdk1.8.0_45 .
我认为我们应该采用StringBuilder附加方法 . 原因是
String连接每次都会创建一个新的字符串对象(As String是不可变对象),因此它将创建3个对象 .
使用“字符串”构建器时,只会创建一个对象[StringBuilder是muttable],并且会向其附加另一个字符串 .
在大多数情况下,您不会看到两种方法之间的实际差异,但很容易构建像这样的最坏情况:
输出是:
问题是to =追加到一个字符串重建一个新的字符串,所以它花费了与字符串长度成线性的东西(两者的总和) .
所以 - 对你的问题:
第二种方法会更快,但它的可读性和维护难度更低 . 正如我所说,在你的具体情况下,你可能不会看到差异 .
从Java 1.5开始,简单的一行连接与“”和StringBuilder.append()生成完全相同的字节码 .
因此,为了代码可读性,请使用“” .
2例外:
循环中的
出于性能原因,不鼓励使用
+=
(String
连接) . 原因是:JavaString
是一个不可变的,每次创建新的连接时都会创建一个新的String
(新的指纹与旧的指纹已经in the String pool不同) . 创建新字符串会给GC带来压力并减慢程序速度:对象创建很昂贵 .下面的代码应该使它更加实用和清晰 .
运行结果报告如下 .
不考虑1连接的结果(JIT尚未完成其工作),即使对于10个连接,性能惩罚也是相关的;对于成千上万的连接,差异是巨大的 .
从这个非常快速的实验中学到的经验(使用上面的代码可以很容易地重现):永远不要使用
+=
将字符串连接在一起,即使在需要一些连接的非常基本的情况下(如上所述,创建新字符串无论如何都是昂贵的并且施加压力GC) .尽可能使toString方法可读!
在我的书中唯一的例外是,如果你能向我证明它消耗了大量资源:)(是的,这意味着分析)
另请注意,Java 5编译器生成的代码比早期Java版本中使用的手写“StringBuffer”方法更快 . 如果你使用“”这个和未来的增强功能是免费的 .
对于我喜欢使用的简单字符串
按顺序,我会说构造字符串的首选方法是使用StringBuilder,String#concat(),然后是重载的运算符 . StringBuilder在使用大型字符串时显着提升性能,就像使用运算符一样,性能大幅下降(随着字符串大小的增加,指数大幅减少) . 使用.concat()的一个问题是它可以抛出NullPointerExceptions .
在Java 9中,版本1应该更快,因为它被转换为
invokedynamic
调用 . 更多细节可以在JEP-280找到:我还与我的老板发生冲突,关于是否使用append或 . 他们正在使用Append(我仍然无法弄清楚他们每次创建新对象时都会说出来) . 所以我想做一些R&D . 虽然我喜欢Michael Borgwardt的解释,但只想展示一个解释,如果有人将来真的需要知道 .
并且上面的类的反汇编出来了
从上面两个代码可以看出迈克尔是对的 . 在每种情况下,只创建一个SB对象 .
版本1更可取,因为它更短,而且没有任何性能差异 .
通常在编译器无法自行替换
StringBuilder
时're concatenating in a loop - that' s .