【问题标题】:Performance between String.format and StringBuilderString.format 和 StringBuilder 之间的性能
【发布时间】:2017-05-22 16:24:12
【问题描述】:

要连接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.Format 是否与StringBuilder 一样高效?哪个更好地连接字符串?为什么?

更新

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

【问题讨论】:

  • 快速基准测试不会花费太多时间
  • 第三个选项:return "Contact {name=" + name + ", lastName=" + lName + ", nickName=" + nick + '}'; 这将创建与使用 StringBuilder 相同的字节码,并且与使用 String.Format 一样可读。
  • 除非您的方法每秒被调用数千次,否则您不会注意到性能差异。您不应该使用 StringBuilder 或 StringBuffer,因为它们的可读性要差得多。我会使用 String.format,因为它非常清楚您的最终字符串的外观。

标签: java stringbuilder string.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

对于非性能关键代码,我更喜欢使用String.format(),因为它使用起来更容易、更愉快。通过简单地查看模式,也可以看到结果字符串的外观。如果我正在编写性能关键代码,或者必须具有低 GC 影响的代码,我会使用 StringBuilder,因为它更快并且可以重复使用。

【讨论】:

  • 请问您是如何运行的?
  • @FarazDurrani 只需使用测试类名称作为参数运行“Launcher”:github.com/SvetlinZarev/com.github.svetlinzarev.jmh.benchmarks/…
  • 谢谢@Svetlin。我能够从谷歌运行其他 JMH。不过还是谢谢。
  • 我实际上认为 StringBuilder 更具可读性且不易出错。我不习惯 String.format() 的符号。而String.format()我需要更加注意itens的顺序,而在StringBuilder中它是每行1个,它更有条理。
【解决方案2】:

在用StringBuilderString.format 做了一些小测试后,我明白他们每个人都需要多少时间来解决连接问题。这里是sn-p代码和结果

代码:

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.format 第一次运行需要更多时间,之后时间会更短,即使由于StringBuilder 而不会变得恒定

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

有了这些结果,可以说StringBuilderString.format效率更高

【讨论】:

  • 您也可以在长字符串和短字符串上测试String,concat
  • 测试循环的有趣想法。似乎解析的格式字符串被缓存并重用,而不是在每次循环迭代时再次解析。此外,由于 StringBuilder 在后续循环迭代中速度较慢,因为您在每次迭代中都实例化了一个全新的 StringBuilder(假设您实际上只是用循环包装了相同的代码)。
【解决方案3】:

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

StringBuilder 代替 String + String

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

【讨论】:

    猜你喜欢
    • 2011-07-23
    • 2018-12-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多