web-dev-qa-db-ja.com

String.formatとStringBuilderの間のパフォーマンス

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.FormatStringBuilderと同じくらい効率的ですか?文字列を連結する方が良いのはなぜですか?

[〜#〜] update [〜#〜]

同様の question をチェックしましたが、質問には答えていません。これまで、文字列を連結するためにStringBuilderを使用しました。または、String.formatを使用する必要がありますか?問題は、どちらが優れているか、なぜですか?

14

StringBuilder vs String.formatで少しテストを行った後、それぞれが連結を解決するのにどれくらいの時間がかかるかがわかりました。ここにスニペットコードと結果

コード:

String name = "stackover";
String lName = " flow";
String nick = " stackoverflow";
String email = "[email protected]";
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 protected], phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
String.format: 38: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
String.format: 51: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}

しかし、ループ内で同じコードを実行すると、結果が変わります。

String.format: 43: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, [email protected], phone=123123123}

String.formatの最初の実行には時間がかかります。その後、StringBuilderの結果として一定にならなくても時間が短くなります

@ G.Fiedlerが言ったように:「String.formatはフォーマット文字列を解析する必要があります...」

これらの結果から、StringBuilderString.formatよりも効率的であると言えます。

6

「より良い」ものは、​​単にあなたの要件に依存します:

  • たとえば_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を使用します。これは、高速で再利用できるためです。

17
Svetlin Zarev

StringBuilderは、String.formatは、フォーマット文字列(複雑なドメイン固有の言語)を解析する必要があります。そしてそれは高価です。

String + Stringの代わりにStringBuilder

ところで:同じバイトコードになるので同じです(Java 1.5)。

8
G. Fiedler