Javaでは、Stringクラスはimmutable(不変)であり、変更操作のたびに新しいオブジェクトが作成されます。StringBuilderとStringBufferクラスはmutable(可変)で、ループ内での連結や複雑な変換に使用されます。
主な違い:
String: immutableで、追加の同期なしでスレッドセーフ。読みやすい不変のテキスト(例えば、キーやメッセージ)に最適。StringBuilder: mutableで、スレッドセーフではないが、迅速な連結が可能。文字列が何度も変更される場合に使用。StringBuffer: mutableで、スレッドセーフ(メソッドが同期化されている)が、StringBuilderよりパフォーマンスで劣る。速度比較の例:
String s = ""; for (int i = 0; i < 10000; i++) { s += i; // 毎回新しいStringを作成するので遅い! } // こちらの方が良い: StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append(i); } String result = sb.toString();
落とし穴:
String.intern()は、多くのユニークな文字列が存在する場合にPermGen/MetaspaceでOutOfMemoryErrorを引き起こす可能性があります。質問: "ループ内での+演算子を用いた文字列の連結は効率的ですか?"
回答: いいえ、毎回新しいStringオブジェクトが作成され、メモリとGCに大きな負担をかけます。多数の操作にはStringBuilderまたは(スレッドセーフ性が必要な場合は)StringBufferを使用する方が良いです。
例:
String s = ""; for (int i = 0; i < 1000; i++) { s += i; // 非常に非効率的! } // vs StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append(i); }
物語
銀行プロジェクトで
+演算子を用いた文字列の連結が大規模なループで実行され、夜間分析が15倍遅くなりました。この問題はStringBuilderへの置き換えで解決され、実行時間は40分から2分に短縮されました。
物語
若い開発者がスレッドに誤りを犯し、同じファサードオブジェクトでStringBuilderを使用しました。このオブジェクトには複数のスレッドが同時にアクセスしており、同期がなかったため、ランダムなバグや不正確な結果を引き起こしました。
物語
プロジェクトでString.intern()を使用してユニークなラベルを作成していました。ユーザー数が数千万に増えると、JVMはOutOfMemoryError: PermGen spaceを発生させました。この問題はintern()の使用をやめ、別のキャッシュメカニズムに移行することで解決されました。