ProgrammingJava開発者

Javaにおける文字列の扱いについて、String/StringBuilder/StringBufferの構造、使用すべき場面、適用時の落とし穴について説明してください。

Hintsage AIアシスタントで面接を突破

回答。

Javaでは、Stringクラスはimmutable(不変)であり、変更操作のたびに新しいオブジェクトが作成されます。StringBuilderStringBufferクラスは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();

落とし穴:

  • immutable文字列の不必要な連結は、メモリリークやパフォーマンスの低下を引き起こします。
  • String.intern()は、多くのユニークな文字列が存在する場合にPermGen/MetaspaceでOutOfMemoryErrorを引き起こす可能性があります。
  • マルチスレッドシナリオでのStringBuilderの誤った使用は、レースコンディションを引き起こす可能性があります。

トリッキーな質問。

質問: "ループ内での+演算子を用いた文字列の連結は効率的ですか?"

回答: いいえ、毎回新しい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()の使用をやめ、別のキャッシュメカニズムに移行することで解決されました。