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
?#1 楼
版本1是更可取的,因为它更短,并且编译器实际上会将其转换为版本2-完全没有性能差异。更重要的是,鉴于我们只有3
属性,
可能没有什么不同,但是您什么时候从concat切换到builder?
在循环连接的那一刻-通常是在编译器无法自行替代
StringBuilder
的情况下。评论
是的,但是语言参考也指出这是可选的。实际上,我只是用JRE 1.6.0_15做了一个简单的测试,并且在反编译类中没有看到任何编译器优化。
–布鲁诺·孔德
09年10月7日在16:07
我只是从问题中尝试了代码(在JDK 1.6.0_16上编译),并发现了预期的优化。我很确定所有现代编译器都可以做到。
–迈克尔·伯格沃德(Michael Borgwardt)
09年10月7日在16:12
你是对的。查看字节码,我可以清楚地看到StringBuilder优化。我正在使用反编译器,并且在某种程度上,它正在转换回concat。 +1
–布鲁诺·孔德
09年10月7日在16:30
并没有打败老手,但是规范中的措辞是:为了提高重复字符串连接的性能,Java编译器_may_使用StringBuffer类或类似的技术来减少通过评估以下内容而创建的中间String对象的数量。一种表达。可能存在关键词。鉴于这是官方可选的(尽管很可能已实施),我们是否应该保护自己?
–卢卡斯
2012年8月14日15:57
@卢卡斯:不,我们不应该。如果编译器决定不执行该优化,那是因为这样做不值得。在99%的情况下,编译器会更好地知道哪种优化是值得的,因此凭经验法则,开发人员不应干预。当然,您的情况可能会降到其他1%,但这只能通过(仔细)基准测试来检查。
–sleske
2012年8月24日在9:33
#2 楼
关键是要在一个地方编写单个串联还是随时间累积它。对于您给出的示例,显式使用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();
评论
是的,StringBuilder不需要一遍又一遍地重新创建String对象。
–奥尔加
2013年9月3日上午10:15
该死的,我用那两个函数来测试我正在工作的大字符串。6.51min vs 11secs
–user1722791
13-10-12在15:56
顺便说一下,您可以使用result + = s;同样(在第一个示例中)
–RAnders00
2015年5月28日13:39
该语句将创建多少个对象? “ {a:” + a +“,b:” + b +“,c:” + c +“}”;
–阿西夫·穆斯塔克(Asif Mushtaq)
16-3-14在12:18
怎么样:String str =(a == null)? null:a'+(b == null)吗? null:b'+(c == null)? c:c'+ ...; ?这会阻止优化的进行吗?
– amitfr
16年7月31日在22:09
#3 楼
在大多数情况下,您不会看到这两种方法之间的实际差异,但是很容易构造这样的最坏情况: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
09年10月7日在16:15
当您对+ =正确时,原始示例是一个序列+,编译器将其转换为单个string.concat调用。您的结果不适用。
–盲目的
2010年6月28日在19:21
@Blindy&Droo:-你们都是正确的。在这种情况下使用.concate是最好的解决方法,因为+ =在每次执行循环例程时都会创建新对象。
–小脑
2011-12-21 20:55
你知道他的toString()不是循环调用的吗?
–欧弥(Omry Yadan)
13年5月5日在2:18
我已经尝试过这个例子来测试速度。所以我的结果是:慢速经过29672毫秒;快速经过15毫秒。因此答案很明显。但是,如果要进行100次迭代-时间是相同的-0毫秒。如果进行500次迭代-16毫秒和0毫秒。等等。
–恩内斯塔斯·格鲁迪斯(Ernestas Gruodis)
13年8月27日在14:38
#4 楼
我更喜欢:String.format( "{a: %s, b: %s, c: %s}", a, b, c );
...,因为它简短易读。
除非您在A内使用它,否则我不会针对速度进行优化。
我同意,如果必须输出很多参数,这种形式可能会造成混淆(就像其中一条评论所述)。在这种情况下,我将切换为更具可读性的形式(也许使用apache-commons的ToStringBuilder-取自matt b的答案)并再次忽略性能。
评论
它实际上更长,包含更多符号,并且变量文本乱序。
– Tom Hawtin-大头钉
09年10月7日在16:11
因此,您会说它比其他方法之一的可读性差吗?
– tangens
09年10月7日在16:16
我更喜欢编写此代码,因为添加更多变量更容易,但是我不确定它是否更具可读性-尤其是随着参数数量的增加。当您需要在不同时间添加位时,它有时也不起作用。
– Alex Feinman
09年10月7日在16:25
似乎更难阅读(对我而言)。现在,我必须在{...}和参数之间来回扫描。
–郭富城
09年10月7日在18:59
我更喜欢这种形式,因为如果参数之一为null是安全的
–rds
13年1月22日在16:13
#5 楼
从Java 1.5开始,使用“ +”和StringBuilder.append()进行简单的一行连接会生成完全相同的字节码。出于代码可读性考虑,请使用“ +”。
2个例外:
多线程环境:StringBuffer
#6 楼
我还与老板就是否使用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对象。
#7 楼
使用最新版本的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 :(
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
有一些明显的区别虽然。在第一种情况下,使用
+
时,将为每个for循环迭代创建新的StringBuilder
,并通过执行toString()
调用(29至41)来存储生成的结果。因此,您将在+
循环中使用for
运算符时生成真正不需要的中间字符串。评论
这是Oracle JDK还是OpenJDK?
–克里斯托弗·鲁西(Christophe Roussy)
16年8月18日在9:23
#8 楼
在Java 9中,版本1应该更快,因为它将转换为invokedynamic
调用。可以在JEP-280中找到更多详细信息:这个想法是用对java.lang.invoke.StringConcatFactory的简单invokedynamic调用来替换整个StringBuilder追加舞蹈。需要连接的值。
#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 avg
10000个串联的平均时间:0.185 ms avg
10000个串联的平均时间: 0.327 ms avg
10000个连接的平均时间:0.501 ms avg
10000个连接的平均时间:0.656 ms avg
在17745 ms中创建的长度为1950000的字符串
[str1 + = str2 ]
10000个连接的平均时间:0.21 ms avg
10000个连接的平均时间:0.652 ms avg
10000个连接的平均时间:1.129 ms avg
10000个连接的平均时间: 1.727 ms avg
10000个串联的平均时间:2.302 ms avg
创建的长度为:1950000的字符串在60279 ms中
[str1.append(str2)]
10000个串联的平均时间:0.002 ms avg
10000 conca的平均时间租约:10000个串联的平均时间0.002 ms avg
:10000个串联的平均时间0.002 ms avg
10000个串联的平均时间0.002 ms avg
10000个串联的平均时间:0.002 ms avg
创建的字符串的长度:1950000 in 100 ms
随着字符串长度的增加,连接时间也随之增加。
这就是
StringBuilder
绝对需要的地方。看到,串联:
UUID.randomUUID()+"---"
,并不会真正影响时间。PS:我不认为何时在Java中使用StringBuilder确实是对此的重复。
这个问题是关于
toString()
多数情况下不执行巨大字符串的串联。2019 Update
自从
java8
次以来,情况有所变化。看来现在(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个连接的平均时间串联:0.255毫秒avg
10000个串联的平均时间:0.336毫秒avg
创建的字符串长度:1950000 in 9147 ms
[str1 + = str2]
10000个串联的平均时间: 0.037 ms avg
10000个并置的平均时间:0.097 ms avg
10000个并置的平均时间:0.249 ms avg
10000个并置的平均时间:0.298 ms avg
10000个并置的平均时间串联:0.326 ms avg
创建的字符串长度:1950000 in 10191 ms
[str1.append(str2)]
10000个串联的平均时间:0.001 ms avg
平均时间10000连拍enations:10000个串联的平均时间0.001 ms avg
:10000个串联的平均时间0.001 ms avg
10000个串联的平均时间0.001 ms avg
10000个串联的平均时间:0.001 ms avg
创建字符串长度:1950000 in 43 ms
值得一提的是,与
bytecode:8/java.version:13
组合相比,bytecode:8/java.version:8
组合具有良好的性能优势评论
这应该是可接受的答案..它取决于决定选择concat还是StringBuilder的String Stream的大小
–user1428716
19-10-31在10:28
@ user1428716仅供参考:答案已更新,但java13中的结果现在有所不同。但我认为主要结论仍然相同。
– Marinos An
19-10-31在13:16
#10 楼
出于性能原因,不建议使用+=
(串联的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带来了压力。 #11 楼
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();
}
或者如果您想使用反射来包含类的每个字段: br />
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
如果需要,还可以自定义ToString的样式。
#12 楼
我的书中唯一的例外是如果您可以向我证明它消耗了大量资源:)(是的,这意味着进行分析)
还要注意,与早期Java版本中使用的手写“ StringBuffer”方法相比,Java 5编译器生成的代码速度更快。如果您使用“ +”,则此功能和将来的增强功能都是免费的。
#13 楼
对于当前的编译器是否仍然需要使用StringBuilder,似乎存在一些争论。所以我想我会给我2美分的经验。我有一个10万条记录的
JDBC
结果集(是的,我需要全部一批。)在我的机器上使用Java 1.8
使用+运算符大约需要5分钟。对于同一查询,使用stringBuilder.append("")
只需不到一秒钟的时间。 所以区别很大。在循环内
StringBuilder
更快。 评论
我认为辩论是关于在循环之外使用它。我认为有一个共识,您需要在循环内使用它。
–乔丹
17年5月26日在18:20
#14 楼
这是我在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
#15 楼
我是否可以指出,如果您要遍历一个集合并使用StringBuilder,则可能要查看Apache Commons Lang和StringUtils.join()(以不同的方式)?不管性能如何,它都省去了创建StringBuilders和for循环的麻烦,这似乎是百万分之一的时间。
#16 楼
在性能上使用'+'进行字符串连接比较昂贵,因为它必须制作一个全新的String副本,因为String在Java中是不可变的。如果连接非常频繁(例如在循环内),则这会发挥特殊作用。以下是我尝试执行此类操作时IDEA的建议:
/>一般规则:
在单个字符串分配中,使用String串联就可以了。
如果要循环构建大量字符数据,请继续StringBuffer。
在字符串上使用+ =总是比使用StringBuffer效率低,因此它会发出警告声-但在某些情况下,与可读性问题相比,所获得的优化可以忽略不计,因此请使用常识。
关于此主题的Jon Skeet博客很好。
评论
除非绝对需要来自多个线程的同步访问,否则永远不要使用StringBuffer。否则,请选择StringBuilder,因为它不是同步的,因此开销较小。
– Err der Loony
19年5月27日在9:30
#17 楼
我认为我们应该使用StringBuilder追加方法。原因是:
字符串连接每次都会创建一个新的字符串对象(因为String是不可变的对象),所以它将创建3个对象。
使用“字符串生成器”,将只创建一个对象[StringBuilder是可变的],并将附加的字符串附加到该对象。
评论
为什么这个答案被否决? docs.oracle.com/javase/8/docs/api/java/util/stream / ...-可变约简
–user1428716
19-10-31在10:23
#18 楼
对于像这样的简单字符串,我更喜欢使用"string".concat("string").concat("string");
依次,我想说构造字符串的首选方法是使用StringBuilder,String#concat(),然后重载的+运算符。当处理大型字符串时,如使用+运算符会大大降低性能(随着String大小的增加呈指数下降),StringBuilder的性能将得到显着提高。使用.concat()的一个问题是它可能引发NullPointerExceptions。
评论
使用concat()的性能可能比'+'更差,因为JLS允许将'+'转换为StringBuilder,并且很可能所有JVM都这样做或使用更有效的替代方法-事实并非如此。 concat必须在您的示例中创建并丢弃至少一个完整的中间字符串。
–劳伦斯·多尔(Lawrence Dol)
09年10月7日在17:27
评论
在什么时候切换到StringBuilder?当它影响内存或性能时。或何时可能。如果您实际上只对几个字符串执行一次,则无需担心。但是,如果要一遍又一遍地进行操作,则在使用StringBuilder时应该会看到可测量的差异。参数中100的平均值是什么?
@UnKnown 100是StringBuilder的初始大小
@nonsequitor那么最大字符将是100?
@Unknown不只是初始大小,如果您知道要处理的字符串的近似大小,则可以告诉StringBuilder要分配多少大小,否则,如果空间不足,则必须通过创建大小将大小加倍一个新的char []数组,然后复制数据-这非常昂贵。您可以通过指定大小来作弊,然后就不需要创建该数组了-因此,如果您认为字符串的长度约为100个字符,则可以将StringBuilder设置为该大小,而不必在内部扩展。 />