Java 中 toString() 中的 StringBuilder 与字符串连接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1532461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
StringBuilder vs String concatenation in toString() in Java
提问by non sequitor
Given the 2 toString()
implementations below, which one is preferred:
鉴于toString()
下面的 2 个实现,哪个是首选:
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();
}
?
?
More importantly, given we have only 3 properties it might not make a difference, but at what point would you switch from +
concat to StringBuilder
?
更重要的是,鉴于我们只有 3 个属性,这可能不会有什么不同,但是您会在什么时候从+
concat切换到 StringBuilder
?
采纳答案by Michael Borgwardt
Version 1 is preferable because it is shorter and the compiler will in fact turn it into version 2- no performance difference whatsoever.
版本 1 更可取,因为它更短,而且编译器实际上会将其转换为版本 2- 没有任何性能差异。
More importantly given we have only 3 properties it might not make a difference, but at what point do you switch from concat to builder?
更重要的是,考虑到我们只有 3 个属性,它可能没有什么不同,但是您在什么时候从 concat 切换到 builder?
At the point where you're concatenating in a loop - that's usually when the compiler can't substitute StringBuilder
by itself.
在您在循环中连接时 - 这通常是编译器无法自行替换StringBuilder
的时候。
回答by joel.neely
The key is whether you are writing a single concatenation all in one place or accumulating it over time.
关键是你是在一个地方编写一个单一的串联还是随着时间的推移积累它。
For the example you gave, there's no point in explicitly using StringBuilder. (Look at the compiled code for your first case.)
对于您给出的示例,显式使用 StringBuilder 毫无意义。(查看第一个案例的编译代码。)
But if you are building a string e.g. inside a loop, use StringBuilder.
但是,如果您正在构建一个字符串,例如在循环内,请使用 StringBuilder。
To clarify, assuming that hugeArray contains thousands of strings, code like this:
澄清一下,假设 hugeArray 包含数千个字符串,代码如下:
...
String result = "";
for (String s : hugeArray) {
result = result + s;
}
is very time- and memory-wasteful compared with:
与以下内容相比非常浪费时间和内存:
...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
sb.append(s);
}
String result = sb.toString();
回答by tangens
I prefer:
我更喜欢:
String.format( "{a: %s, b: %s, c: %s}", a, b, c );
...because it's short and readable.
...因为它简短易读。
I would notoptimize this for speed unless you use it inside a loop with a very high repeat count andhave measured the performance difference.
除非您在具有非常高重复计数的循环中使用它并测量了性能差异,否则我不会优化它的速度。
I agree, that if you have to output a lot of parameters, this form can get confusing (like one of the comments say). In this case I'd switch to a more readable form (perhaps using ToStringBuilderof apache-commons - taken from the answer of matt b) and ignore performance again.
我同意,如果您必须输出大量参数,则此表单可能会令人困惑(就像其中一条评论所说的那样)。在这种情况下,我会切换到更易读的形式(也许使用apache-commons 的ToStringBuilder- 取自 matt b 的答案)并再次忽略性能。
回答by Omry Yadan
In most cases, you won't see an actual difference between the two approaches, but it's easy to construct a worst case scenario like this one:
在大多数情况下,您不会看到两种方法之间的实际差异,但很容易构建像这样的最坏情况:
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+="*";
}
}
The output is:
输出是:
slow elapsed 11741 ms
fast elapsed 7 ms
The problem is that to += append to a string reconstructs a new string, so it costs something linear to the length of your strings (sum of both).
问题是将 += 附加到字符串会重建一个新字符串,因此它的成本与字符串的长度(两者之和)成线性关系。
So - to your question:
所以 - 对于你的问题:
The second approach would be faster, but it's less readable and harder to maintain. As I said, in your specific case you would probably not see the difference.
第二种方法会更快,但可读性较差且难以维护。正如我所说,在您的具体情况下,您可能看不到差异。
回答by Brian Agnew
Can I point out that if you're going to iterate over a collection and use StringBuilder, you may want to check out Apache Commons Langand StringUtils.join()(in different flavours) ?
我能否指出,如果您要迭代一个集合并使用 StringBuilder,您可能需要查看Apache Commons Lang和StringUtils.join()(不同的风格)?
Regardless of performance, it'll save you having to create StringBuilders and for loops for what seems like the millionthtime.
无论性能如何,它都会让您不必创建 StringBuilders 和 for 循环,这似乎是百万次。
回答by matt b
Apache Commons-Lang has a ToStringBuilderclass which is super easy to use. It does a nice job of both handling the append-logic as well as formatting of how you want your toString to look.
Apache Commons-Lang 有一个非常容易使用的ToStringBuilder类。它在处理附加逻辑以及您希望 toString 的外观格式方面做得很好。
public void toString() {
ToStringBuilder tsb = new ToStringBuilder(this);
tsb.append("a", a);
tsb.append("b", b)
return tsb.toString();
}
Will return output that looks like com.blah.YourClass@abc1321f[a=whatever, b=foo]
.
将返回看起来像com.blah.YourClass@abc1321f[a=whatever, b=foo]
.
Or in a more condensed form using chaining:
或者以更简洁的形式使用链接:
public void toString() {
return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}
Or if you want to use reflection to include every field of the class:
或者,如果您想使用反射来包含类的每个字段:
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
You can also customize the style of the ToString if you want.
如果需要,您还可以自定义 ToString 的样式。
回答by Droo
For simple strings like that I prefer to use
对于像这样的简单字符串,我更喜欢使用
"string".concat("string").concat("string");
In order, I would say the preferred method of constructing a string is using StringBuilder, String#concat(), then the overloaded + operator. StringBuilder is a significant performance increase when working large strings just like using the + operator is a large decrease in performance (exponentially large decrease as the String size increases). The one problem with using .concat() is that it can throw NullPointerExceptions.
按照顺序,我会说构造字符串的首选方法是使用 StringBuilder、String#concat(),然后是重载的 + 运算符。StringBuilder 在处理大字符串时显着提高性能,就像使用 + 运算符会导致性能大幅下降(随着字符串大小的增加呈指数级大幅下降)。使用 .concat() 的一个问题是它会抛出 NullPointerExceptions。
回答by Thorbj?rn Ravn Andersen
Make the toString method as readable as you possibly can!
尽可能使 toString 方法具有可读性!
The sole exception for this in my book is if you can proveto me that it consumes significant resources :) (Yes, this means profiling)
这在我的书中唯一的例外是,如果你能证明,我认为它消耗显著资源:)(是的,这个手段剖析)
Also note that the Java 5 compiler generates faster code than the handwritten "StringBuffer" approach used in earlier versions of Java. If you use "+" this and future enhancements comes for free.
另请注意,Java 5 编译器生成的代码比 Java 早期版本中使用的手写“StringBuffer”方法更快。如果您使用“+”,则此功能和未来的增强功能都是免费的。
回答by perilbrain
I also had clash with my boss on the fact whether to use append or +.As they are using Append(I still cant figure out as they say every time a new object is created). So I thought to do some R&D.Although I love Michael Borgwardt explaination but just wanted to show an explanation if somebody will really need to know in future.
我还与我的老板在是否使用 append 或 + 的问题上发生冲突。因为他们正在使用 Append(我仍然无法弄清楚每次创建新对象时他们所说的)。所以我想做一些 R&D。虽然我喜欢 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());
}
}
and disassembly of above class comes out as
和上面的类的反汇编出来
.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
; ---------------------------------------------------------------------------
From the above two codes you can see Michael is right.In each case only one SB object is created.
从上面两段代码可以看出Michael是对的。在每种情况下,只创建了一个SB对象。
回答by Zofren
Since Java 1.5, simple one line concatenation with "+" and StringBuilder.append() generate exactly the same bytecode.
从 Java 1.5 开始,用“+”和 StringBuilder.append() 简单的一行连接生成完全相同的字节码。
So for the sake of code readability, use "+".
所以为了代码可读性,使用“+”。
2 exceptions :
2个例外:
- multithreaded environment : StringBuffer
- concatenation in loops : StringBuilder/StringBuffer
- 多线程环境:StringBuffer
- 循环串联:StringBuilder/StringBuffer