Java String.format和StringBuilder之间的性能

Java String.format和StringBuilder之间的性能,java,stringbuilder,string.format,Java,Stringbuilder,String.format,为了连接String,我们通常使用StringBuilder而不是String+String,但是我们也可以对String.format执行相同的操作,它通过给定的区域设置、格式和参数返回格式化字符串 示例: 将字符串与StringBuilder连接起来 String concatenateStringWithStringBuilder(String name, String lName, String nick) { final StringBuilder sb = new Strin

为了连接
String
,我们通常使用
StringBuilder
而不是
String
+
String
,但是我们也可以对
String.format
执行相同的操作,它通过给定的区域设置、格式和参数返回格式化字符串

示例:

将字符串与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 name = "stackover";
String lName = " flow";
String nick = " stackoverflow";
String email = "stackoverflow@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
是否与
StringBuilder
一样高效?哪一种连接字符串更好?为什么

更新


我检查了类似的问题,但没有回答我的问题。到目前为止,我已经使用了
StringBuilder
来连接字符串,是否应该使用?还是应该使用
String.format
?问题是哪个更好,为什么?

StringBuilder
更快,因为
String.format
必须解析格式字符串(一种复杂的特定于域的语言)。而且很贵

StringBuilder,而不是String+String


顺便说一句:这是一样的,因为它会产生相同的字节码(从Java 1.5开始)。

在使用
StringBuilder
vs
String.format进行了一点测试后,我了解了解决连接问题需要多少时间。这里是代码片段和结果

代码:

String concatenateStringWithStringFormat(String name, String lName, String nick) {
    return String.format("Contact {name=%s, lastName=%s, nickName=%s}", name, lName, nick);
}
String name = "stackover";
String lName = " flow";
String nick = " stackoverflow";
String email = "stackoverflow@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: 43: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
第一次运行
String.format
会花费更多的时间,之后时间会更短,即使由于
StringBuilder

正如@G.Fiedler所说:“
String.format
必须解析格式字符串…”

根据这些结果,可以说
StringBuilder
String更有效。格式

什么“更好”完全取决于您的要求:

  • 例如,
    stringbuilder
    将更快,但代码将更不可读,并且更容易出错

  • 另一方面,
    String.format()
    以牺牲性能为代价生成更可读的代码

JMH基准来说明性能差异(注意字符串生成器代码较长,很难理解结果字符串的外观):

结果是:

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
,因为它更快,而且可以重用。

快速基准测试不会花费太多时间。可能会重复第三个选项:
返回“Contact{name=“+name+”,lastName=“+lName+”,昵称=“+nick+”}这将创建与使用StringBuilder相同的字节码,并且与使用String.Format一样可读。请参见此,除非每秒调用数千次您的方法,否则您不会注意到性能上的差异。您不应该使用StringBuilder或StringBuffer,因为它们的可读性要差得多。我会使用String.format,因为它非常清楚最终的字符串是什么样子的。您还可以在长字符串和短字符串上测试
String,concat
,测试循环。似乎解析的格式字符串被缓存和重用,而不是在每次循环迭代中再次解析。另外,由于StringBuilder在后续循环迭代中的速度较慢,因此它会使您的性能提高,因为您在每次迭代中都会实例化一个全新的StringBuilder(假设您实际上只是用循环包装了相同的代码)。我想问一下,您是如何运行它的?@FarazDurrani只是以测试类名作为参数运行“启动器”:谢谢@Svetlin。我能够从谷歌运行其他JMH。不过谢谢你。