String.format 和 StringBuilder 之间的性能
Performance between String.format and StringBuilder
为了连接 String
我们经常使用 StringBuilder
而不是 String
+ String
,但我们也可以用 String.format
做同样的事情 returns 给定语言环境、格式和参数的格式化字符串。
示例:
用 StringBuilder 连接字符串
String concatenateStringWithStringBuilder(String name, String lName, String nick) {
final StringBuilder sb = new StringBuilder("Contact {");
sb.append(", name='").append(name)
.append(", lastName='").append(lName)
.append(", nickName='").append(nick)
.append('}');
return sb.toString();
}
使用 StringFormat 连接字符串:
String concatenateStringWithStringFormat(String name, String lName, String nick) {
return String.format("Contact {name=%s, lastName=%s, nickName=%s}", name, lName, nick);
}
在性能方面,String.Format
和 StringBuilder
一样高效吗?哪个更适合连接字符串,为什么?
更新
我检查了类似的 question,但没有回答我的问题。到目前为止,我已经使用 StringBuilder
来连接字符串,我应该按照它使用吗?或者我应该使用 String.format
?问题是哪个更好,为什么?
StringBuilder
更快,因为 String.format
必须解析格式字符串(一种复杂的领域特定语言)。而且很贵。
StringBuilder instead of String + String
顺便说一句:它是一样的,因为它产生相同的字节码(自 Java 1.5)。
在对 StringBuilder
和 String.format
进行了一些测试后,我了解了他们每个人需要多少时间来解决串联问题。这里是代码片段和结果
代码:
String name = "stackover";
String lName = " flow";
String nick = " Whosebug";
String email = "Whosebug@email.com";
int phone = 123123123;
//for (int i = 0; i < 10; i++) {
long initialTime1 = System.currentTimeMillis();
String response = String.format(" - Contact {name=%s, lastName=%s, nickName=%s, email=%s, phone=%d}",
name, lName, nick, email, phone);
long finalTime1 = System.currentTimeMillis();
long totalTime1 = finalTime1 - initialTime1;
System.out.println(totalTime1 + response);
long initialTime2 = System.currentTimeMillis();
final StringBuilder sb = new StringBuilder(" - Contact {");
sb.append("name=").append(name)
.append(", lastName=").append(lName)
.append(", nickName=").append(nick)
.append(", email=").append(email)
.append(", phone=").append(phone)
.append('}');
String response2 = sb.toString();
long finalTime2 = System.currentTimeMillis();
long totalTime2 = finalTime2 - initialTime2;
System.out.println(totalTime2 + response2);
//}
在 运行 代码多次之后,我看到 String.format
需要更多时间:
String.format: 46: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 38: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 51: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
但是如果我 运行 在一个循环中使用相同的代码,结果就会改变。
String.format: 43: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
第一次 String.format
运行s 需要更多时间,之后时间更短,即使它不会因为 StringBuilder
[=21 而变得恒定=]
正如@G.Fiedler 所说:“String.format
必须解析格式字符串...”
根据这些结果可以说 StringBuilder
比 String.format
更有效
什么是"better"完全取决于您的要求:
比如String Builder
会更快,但是代码会更难读,也更容易出错。
另一方面,String.format()
以牺牲性能为代价生成更易读的代码。
用于说明性能差异的 JMH 基准(请注意,字符串生成器代码较长且很难理解生成的字符串的样子):
@Fork(1)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 10)
@BenchmarkMode(Mode.Throughput)
public class StringFormatBenchmark {
private String name = "UserName";
private String lName = "LUserName";
private String nick = "UserNick";
@Benchmark
public void stringFormat(Blackhole blackhole) {
final String result = String.format("Contact {name=%s, lastName=%s, nickName=%s}", name, lName, nick);
blackhole.consume(result);
}
@Benchmark
public void stringBuilder(Blackhole blackhole) {
final StringBuffer sb = new StringBuffer("Contact {");
sb.append(", name='").append(name)
.append(", lastName='").append(lName)
.append(", nickName='").append(nick)
.append('}');
final String result = sb.toString();
blackhole.consume(result);
}
}
结果:
Benchmark Mode Cnt Score Error Units
StringFormatBenchmark.stringBuilder thrpt 10 10617.210 ± 157.302 ops/ms
StringFormatBenchmark.stringFormat thrpt 10 960.658 ± 7.398 ops/ms
对于非性能关键代码,我更喜欢使用 String.format()
,因为它使用起来更容易、更愉快。通过简单地查看模式,也可以看到结果字符串的样子。如果我正在执行性能关键代码,或者必须具有低 GC 影响的代码,我会使用 StringBuilder
因为它更快并且可以重用。
为了连接 String
我们经常使用 StringBuilder
而不是 String
+ String
,但我们也可以用 String.format
做同样的事情 returns 给定语言环境、格式和参数的格式化字符串。
示例:
用 StringBuilder 连接字符串
String concatenateStringWithStringBuilder(String name, String lName, String nick) {
final StringBuilder sb = new StringBuilder("Contact {");
sb.append(", name='").append(name)
.append(", lastName='").append(lName)
.append(", nickName='").append(nick)
.append('}');
return sb.toString();
}
使用 StringFormat 连接字符串:
String concatenateStringWithStringFormat(String name, String lName, String nick) {
return String.format("Contact {name=%s, lastName=%s, nickName=%s}", name, lName, nick);
}
在性能方面,String.Format
和 StringBuilder
一样高效吗?哪个更适合连接字符串,为什么?
更新
我检查了类似的 question,但没有回答我的问题。到目前为止,我已经使用 StringBuilder
来连接字符串,我应该按照它使用吗?或者我应该使用 String.format
?问题是哪个更好,为什么?
StringBuilder
更快,因为 String.format
必须解析格式字符串(一种复杂的领域特定语言)。而且很贵。
StringBuilder instead of String + String
顺便说一句:它是一样的,因为它产生相同的字节码(自 Java 1.5)。
在对 StringBuilder
和 String.format
进行了一些测试后,我了解了他们每个人需要多少时间来解决串联问题。这里是代码片段和结果
代码:
String name = "stackover";
String lName = " flow";
String nick = " Whosebug";
String email = "Whosebug@email.com";
int phone = 123123123;
//for (int i = 0; i < 10; i++) {
long initialTime1 = System.currentTimeMillis();
String response = String.format(" - Contact {name=%s, lastName=%s, nickName=%s, email=%s, phone=%d}",
name, lName, nick, email, phone);
long finalTime1 = System.currentTimeMillis();
long totalTime1 = finalTime1 - initialTime1;
System.out.println(totalTime1 + response);
long initialTime2 = System.currentTimeMillis();
final StringBuilder sb = new StringBuilder(" - Contact {");
sb.append("name=").append(name)
.append(", lastName=").append(lName)
.append(", nickName=").append(nick)
.append(", email=").append(email)
.append(", phone=").append(phone)
.append('}');
String response2 = sb.toString();
long finalTime2 = System.currentTimeMillis();
long totalTime2 = finalTime2 - initialTime2;
System.out.println(totalTime2 + response2);
//}
在 运行 代码多次之后,我看到 String.format
需要更多时间:
String.format: 46: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 38: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 51: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
但是如果我 运行 在一个循环中使用相同的代码,结果就会改变。
String.format: 43: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= Whosebug, email=Whosebug@email.com, phone=123123123}
第一次 String.format
运行s 需要更多时间,之后时间更短,即使它不会因为 StringBuilder
[=21 而变得恒定=]
正如@G.Fiedler 所说:“String.format
必须解析格式字符串...”
根据这些结果可以说 StringBuilder
比 String.format
什么是"better"完全取决于您的要求:
比如
String Builder
会更快,但是代码会更难读,也更容易出错。另一方面,
String.format()
以牺牲性能为代价生成更易读的代码。
用于说明性能差异的 JMH 基准(请注意,字符串生成器代码较长且很难理解生成的字符串的样子):
@Fork(1)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 10)
@BenchmarkMode(Mode.Throughput)
public class StringFormatBenchmark {
private String name = "UserName";
private String lName = "LUserName";
private String nick = "UserNick";
@Benchmark
public void stringFormat(Blackhole blackhole) {
final String result = String.format("Contact {name=%s, lastName=%s, nickName=%s}", name, lName, nick);
blackhole.consume(result);
}
@Benchmark
public void stringBuilder(Blackhole blackhole) {
final StringBuffer sb = new StringBuffer("Contact {");
sb.append(", name='").append(name)
.append(", lastName='").append(lName)
.append(", nickName='").append(nick)
.append('}');
final String result = sb.toString();
blackhole.consume(result);
}
}
结果:
Benchmark Mode Cnt Score Error Units
StringFormatBenchmark.stringBuilder thrpt 10 10617.210 ± 157.302 ops/ms
StringFormatBenchmark.stringFormat thrpt 10 960.658 ± 7.398 ops/ms
对于非性能关键代码,我更喜欢使用 String.format()
,因为它使用起来更容易、更愉快。通过简单地查看模式,也可以看到结果字符串的样子。如果我正在执行性能关键代码,或者必须具有低 GC 影响的代码,我会使用 StringBuilder
因为它更快并且可以重用。