文字列があると仮定すると、この文字列に対して多くのループ スプライシング操作を実行します。「+」を使用するとパフォーマンスが最低になります。しかし、このパフォーマンスはどれほどひどいのでしょうか? StringBuffer、StringBuilder、または String.concat() をパフォーマンス テストに追加した場合、結果はどうなるでしょうか?この記事ではそんな疑問にお答えします!
Per4j を使用してパフォーマンスを計算します。このツールは、所要時間の最小値と最大値、統計期間の標準偏差など、パフォーマンス指標の完全なセットを提供できるからです。テスト コードでは、正確な標準偏差値を取得するために、20 個のスプライシング "*" のテストを 50,000 回実行します。文字列を連結するために使用するメソッドは次のとおりです。
次のようにコードをコピーします。
連結演算子 (+)
文字列 concat メソッド concat(String str)
StringBuffer の追加メソッド append(String str)
StringBuilder の追加メソッド append(String str)
最後に、バイトコードを見て、これらのメソッドがどのように正確に実行されるかを確認します。それでは、クラスを作成してみましょう。各ループのパフォーマンスを計算するには、コード内の各テスト コードを Per4J ライブラリでラップする必要があることに注意してください。まず反復回数を定義します
次のようにコードをコピーします。
プライベート静的最終整数 OUTER_ITERATION=20;
プライベート静的最終整数 INNER_ITERATION=50000;
次に、上記の 4 つの方法を使用してテスト コードを実装します。
次のようにコードをコピーします。
文字列addTestStr = "";
文字列 concatTestStr = "";
StringBuffer concatTestSb = null;
StringBuilder concatTestSbu = null;
for (int innerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringAddConcat");
addTestStr = "";
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
addTestStr += "*";
stopWatch.stop();
}
for (int innerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringConcat");
concatTestStr = "";
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestStr.concat("*");
stopWatch.stop();
}
for (int innerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBufferConcat");
concatTestSb = 新しい StringBuffer();
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSb.append("*");
stopWatch.stop();
}
for (int innerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBuilderConcat");
concatTestSbu = 新しい StringBuilder();
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSbu.append("*");
stopWatch.stop();
}
次にプログラムを実行してパフォーマンス メトリックを生成します。私のオペレーティング環境は、64 ビット Windows 7 オペレーティング システム、4 GB メモリとデュアルコア Quad 2.00 GHz CPU を搭載した 32 ビット JVM (7-ea) マシンです。
まさに私たちの想像通りの仕上がりになりました。唯一興味深いのは、String.concat も非常に優れている理由です。String が定数クラス (初期化後に変更されないクラス) であることは誰もが知っていますが、なぜ concat のパフォーマンスが優れているのでしょうか。 (翻訳者注: 実は、原著者のテスト コードに問題があります。concat() メソッドのテスト コードは、concatTestStr=concatTestStr.concat(“*”) のように記述する必要があります。) これに答えるには、質問ですが、concat 逆コンパイルを見てみる必要があります。バイトコードが出てきます。この記事のダウンロード パッケージにはすべてのバイトコードが含まれていますが、次に concat のコード スニペットを見てみましょう。
次のようにコードをコピーします。
46: 新しい #6; //クラス java/lang/StringBuilder
49: ダップ
50: invokespecial #7; //メソッド java/lang/StringBuilder."<init>":()V;
53: ロード_1
54: invokevirtual #8; //メソッド java/lang/StringBuilder.append:
(Ljava/lang/String;)Ljava/lang/StringBuilder;
57: ldc #9; //文字列 *
59: invokevirtual #8; //メソッド java/lang/StringBuilder.append:
(Ljava/lang/String;)Ljava/lang/StringBuilder;
62: invokevirtual #10; //メソッド java/lang/StringBuilder.toString:()
Ljava/lang/文字列;
65: アストア_1
66: iinc 7, 1
69: 38に進む
このコードは String.concat() のバイトコードです。このコードから、concat() メソッドが StringBuilder と同じくらい優れたパフォーマンスを発揮することがわかりますが、追加の StringBuilder と.append(str).append(str).toString() 操作を行うと concate のパフォーマンスに影響するため、StringBuilder と Stringキャンケートタイムは1.8と3.3です。
したがって、最も単純な連結を行う場合でも、StringBuffer または StringBuilder インスタンスを作成したくない場合は、concat を使用する必要があります。ただし、文字列の結合操作が多数ある場合は、 concat を使用しないでください (翻訳者注: テスト コードは機能的に完全に同等ではないため、置き換えられたテスト コードの concat の平均処理時間は 1650.9 ミリ秒です。この結果は元のテキストのコメント)。concat はプログラムのパフォーマンスを低下させ、CPU を消費するためです。したがって、スレッドの安全性や同期を考慮せずに最高のパフォーマンスを得るには、StringBuilder を使用する必要があります。