String.format 和 StringBuilder 之间的性能

2022-09-01 03:07:01

为了连接,我们经常使用代替+,但我们也可以做同样的事情,通过给定的区域设置,格式和参数返回格式化的字符串。StringStringBuilderStringStringString.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.FormatStringBuilder

更新

我检查了类似的问题,但没有回答我的问题。到目前为止,我已经习惯了连接字符串,我应该使用它吗?或者我应该使用?问题是哪一个更好,为什么?StringBuilderString.format


答案 1

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

  • 例如,会更快,但代码将更加不可读,并且更容易犯错误。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

对于非性能关键代码,我更喜欢使用 ,因为它使用起来更容易、更愉快。此外,只需查看模式,即可看到生成的字符串的外观。如果我正在做一个性能关键代码,或者一些必须具有低GC影响的东西,我会使用一个,因为它更快并且可以重用。String.format()StringBuilder


答案 2

在用vs做了一个小测试之后,我明白了他们每个人需要多少时间来解决串联问题。此处为代码段代码和结果StringBuilderString.format

法典:

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

String.format: 46: 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: 38: 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: 51: 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: 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.formatStringBuilder

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

有了这些结果,可以说比StringBuilderString.format