文字列(String)vs 文字列バッファ(StringBuffer)vs 文字列ビルダー(StringBuilder)
Javaで最も広く使用されているクラスの一つはStringです。StringBufferとStringBuilderクラスは文字列を操作するためのメソッドを提供しています。StringBufferとStringBuilderの違いについて調べてみましょう。StringBuffer対StringBuilderは、人気のあるJavaのインタビューの質問です。
文字列 vs StringBuffer vs StringBuilder
文字列は、Javaのコアの面接において最も重要なトピックの一つです。もしコンソールに何か出力するプログラムを書く場合、文字列を使用します。このチュートリアルでは、文字列クラスの主要な機能に焦点を当てます。その後、StringBufferとStringBuilderクラスを比較します。
Javaの文字列
-
- Stringクラスは文字列を表し、2つの方法でStringをインスタンス化することができます。
-
- String str = “ABC”;
-
- // または
-
- String str = new String(“ABC”);
JavaではStringは不変です。したがって、マルチスレッド環境で使用するのに適しています。データの不整合の心配がないため、関数間で共有することができます。
ダブルクォーテーションを使用してStringを作成すると、JVMはまず文字列プール内の同じ値を持つStringを検索します。見つかれば、プールからの文字列オブジェクトの参照を返します。見つからない場合は、Stringオブジェクトを文字列プールに作成して参照を返します。JVMは、異なるスレッドで同じStringを使用することで多くのメモリを節約します。
new演算子を使用して文字列を作成する場合、それはヒープメモリに作成されます。
+演算子はStringにオーバーロードされています。2つの文字列を連結するために使用することができます。内部的には、これを実行するためにStringBufferを使用します。
Stringはequals()およびhashCode()メソッドをオーバーライドしています。2つのStringは、文字のシーケンスが同じ場合にのみ等しいです。equals()メソッドは大文字と小文字を区別します。大文字と小文字を区別しないチェックを行いたい場合は、equalsIgnoreCase()メソッドを使用する必要があります。
文字列は文字ストリームのUTF-16エンコーディングを使用します。
Stringはfinalクラスです。すべてのフィールドは”private int hash”を除いてfinalです。このフィールドにはhashCode()関数の値が含まれています。ハッシュコードの値は、hashCode()メソッドが最初に呼び出されたときにのみ計算され、このフィールドにキャッシュされます。さらに、ハッシュはStringクラスのfinalフィールドを使用していくつかの計算を行って生成されます。したがって、hashCode()メソッドが呼び出されるたびに、同じ出力が得られます。呼び出し元にとっては、毎回計算が行われているように見えますが、内部的にはハッシュフィールドにキャッシュされています。
文字列 vs StringBuffer
JavaではStringはimmutable(変更不可)であるため、文字列の操作(連結、部分文字列など)を行うたびに新しいStringが生成され、古いStringはガーベジコレクションのために破棄されます。これらは重い操作であり、ヒープに多くのガベージを生成します。そのため、JavaではStringの操作にはStringBufferとStringBuilderクラスが提供されています。StringBufferとStringBuilderはJavaでmutable(変更可能)なオブジェクトです。文字列の操作のために、append()、insert()、delete()、substring()メソッドを提供しています。
StringBufferとStringBuilderの比較
Java 1.4まで、文字列の操作にはStringBufferが唯一の選択肢でした。しかし、その欠点として、全ての公開メソッドが同期されているという点があります。StringBufferはスレッドの安全性を提供する反面、パフォーマンスにコストがかかります。ほとんどの場合、文字列はマルチスレッドの環境で使用されることはありません。そのため、Java 1.5では新たにStringBuilderというクラスが導入されました。StringBuilderはStringBufferと似ていますが、スレッドの安全性と同期の点が異なります。StringBufferにはsubstring、length、capacity、trimToSizeなどの余分なメソッドがありますが、これらはStringにも存在するため必要ありません。そのため、これらのメソッドはStringBuilderクラスには実装されていません。StringBufferはJava 1.0で導入されましたが、StringBuilderクラスはStringBufferの欠点を見てからJava 1.5で導入されました。もしシングルスレッドの環境にいる場合、もしくはスレッドの安全性を気にしない場合は、StringBuilderを使用すべきです。それ以外の場合は、スレッドセーフな操作にはStringBufferを使用してください。
StringBuilderとStringBufferのパフォーマンスの比較
私は、StringBufferとStringBuilderオブジェクトに対して複数回のappend()操作を行うサンプルプログラムとの同期の効果をパフォーマンス面で調査しようとしています。
package com.scdev.java;
import java.util.GregorianCalendar;
public class TestString {
public static void main(String[] args) {
System.gc();
long start=new GregorianCalendar().getTimeInMillis();
long startMemory=Runtime.getRuntime().freeMemory();
StringBuffer sb = new StringBuffer();
//StringBuilder sb = new StringBuilder();
for(int i = 0; i<10000000; i++){
sb.append(":").append(i);
}
long end=new GregorianCalendar().getTimeInMillis();
long endMemory=Runtime.getRuntime().freeMemory();
System.out.println("Time Taken:"+(end-start));
System.out.println("Memory used:"+(startMemory-endMemory));
}
}
私は時間とメモリの値を確認するため、StringBufferオブジェクトに対しても同じコードを実行しました。各ケースについてコードを5回実行し、平均値を計算しました。
Value of i | StringBuffer (Time, Memory) | StringBuilder (Time, Memory) |
---|---|---|
10,00,000 | 808, 149356704 | 633, 149356704 |
1,00,00,000 | 7448, 147783888 | 6179, 147783888 |
単一スレッドの環境でも、StringBuilderの方が明らかにStringBufferよりも優れたパフォーマンスを発揮しています。このパフォーマンスの差は、StringBufferメソッドにおける同期処理によって引き起こされる可能性があります。
StringとStringBufferとStringBuilderの違い。
-
- Stringは不変(immutable)であり、StringBufferやStringBuilderは可変(mutable)なクラスです。
-
- StringBufferはスレッドセーフ(thread-safe)であり、同期されていますが、StringBuilderはそうではありません。そのため、StringBuilderはStringBufferよりも高速です。
-
- 文字列の連結演算子(+)は内部的にStringBufferまたはStringBuilderクラスを使用しています。
- マルチスレッド環境での文字列操作では、StringBuilderを使用しない限り、StringBufferクラスを使用するべきです。
String, StringBuffer, および StringBuilder の違いについて、短いまとめは以上です。一般的なプログラミングのシナリオでは、StringBuilder が StringBuffer よりも適しています。参考文献: [References]
- String API Doc
- StringBuffer API Doc
- StringBuilder API Doc