ホームページ  >  記事  >  Java  >  JavaのString、StringBuffer、StringBuilderの違いの分析

JavaのString、StringBuffer、StringBuilderの違いの分析

高洛峰
高洛峰オリジナル
2017-01-22 11:41:361422ブラウズ

String と StringBuffer の違いについては誰もがよく理解していると思いますが、これら 2 つのクラスの動作原理についてはまだよくわかっていない人が多いと思われます。ところで、今日はこの概念について説明します。 J2SE 5.0 で導入された新しい文字操作クラス - StringBuilder。では、この StringBuilder および StringBuffer と、最初に出会った String クラスとの違いは何でしょうか?さまざまな状況でどれを使用する必要がありますか?これらのカテゴリーについて私の意見を共有したいと思います。また、誰もが間違いを犯しているので、それを修正しながら学ぶ良い機会になることを願っています。

簡単に言うと、String 型と StringBuffer 型の主なパフォーマンスの違いは、String が不変オブジェクトであることです (なぜですか? Java の設計者に聞いてください。なぜ String がネイティブ型ではないのか?)。変更する場合、実際には、新しい String オブジェクトを生成し、その新しい String オブジェクトを指すポインターを指すのと同じです。したがって、オブジェクトが変更されるたびに内容が頻繁に変更される文字列には String を使用しないことをお勧めします。特にメモリ内に参照されていないオブジェクトが多すぎると、JVM の GC が動作し始め、速度が確実に遅くなります。以下はあまり適切ではない例です:

String S1 = "abc"; 
For(int I = 0 ; I < 10000 ; I ++) // For 模拟程序的多次调用 
{ 
S1 + = "def"; 
S1 = "abc"; 
}

この場合、for ループが完了した後、メモリ内のオブジェクトが GC によってクリーンアップされていない場合、メモリ内には 20,000 を超えるオブジェクトが存在することになります。メモリという驚異的な数があり、多くの人が使用するシステムであれば、その数はそれほど多くないため、使用する際には誰もが注意する必要があります。

StringBuffer クラスを使用する場合、結果は毎回異なります。新しいオブジェクトを生成してオブジェクト参照を変更するのではなく、StringBuffer オブジェクト自体に対する操作になります。したがって、一般に、特に文字列オブジェクトが頻繁に変更される場合には、StringBuffer を使用することをお勧めします。一部の特殊なケースでは、String オブジェクトの文字列連結は、実際には JVM によって StringBuffer オブジェクトの連結として解釈されるため、このような場合、String オブジェクトの速度が StringBuffer オブジェクトの速度よりも遅くなることはなく、特に次の文字列オブジェクトは生成された文字列の効率は StringBuffer よりもはるかに高速です:

String S1 = "This is only a" + " simple" + " test"; 
StringBuffer Sb = new StringBuilder("This is only a").append(" simple").append(" test");

String S1 オブジェクトの生成速度が単純に速すぎることに驚くでしょう。現時点では、StringBuffer には速度の点でまったく利点がありません。実際、これは JVM のトリックであり、JVM から見ると、これは

String S1 = "This is only a" + " simple" + "test"; 其实就是: String S1 = "This is only a simple test"; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: 
String S2 = "This is only a"; 
String S3 = " simple"; 
String S4 = " test"; 
String S1 = S2 +S3 + S4;

このとき、JVM は独自の方法で実行するため、S1 オブジェクトの生成速度は以前ほど速くなくなります。後でテストして確認できます。

ここから、最初の結論が得られます: ほとんどの場合、StringBuffer > String

そして、StringBuilder はそれらとどう比較されるのでしょうか?まず簡単に紹介します。 StringBuilder は JDK5.0 で新しく追加されたクラスです。 StringBuffer との違いは次のとおりです (ソース JavaWorld):

Java.lang.StringBuffer はスレッドセーフな変数文字列です。 String に似た文字列バッファですが、変更できません。文字列バッファは複数のスレッドで安全に使用できます。これらのメソッドは必要に応じて同期できるため、特定のインスタンスに対するすべての操作が、関連する各スレッドによって行われるメソッド呼び出しの順序と一致するシリアル順序で発生するように見えます。

各文字列バッファには特定の容量があります。文字列バッファーに含まれる文字シーケンスの長さがこの容量を超えない限り、新しい内部バッファー配列を割り当てる必要はありません。内部バッファがオーバーフローした場合、この容量は自動的に増加します。 JDK 5.0 以降、シングル スレッドで使用するための同等のクラス StringBuilder がこのクラスに追加されました。 StringBuilder クラスは、すべて同じ操作をサポートしますが、同期を実行しないため高速であるため、通常はこのクラスよりも優先して使用する必要があります。

しかし、複数のスレッドで StringBuilder のインスタンスを使用するのは安全ではありません。このような同期が必要な場合は、StringBuffer を使用することをお勧めします。

これらの違いは誰でも理解できると思いますので、以下に一般的な導出を行ってみましょう:

ほとんどの場合、StringBuilder > StringBuffer

したがって、この不等式の伝達定理によれば、次のようになります。 StringBuffer > String

このような導出結果が得られたので、それを検証するためのテストを実行してみましょう:

テスト コードは次のとおりです:

public class testssb { 

/** Creates a new instance of testssb */ 
final static int ttime = 10000;// 测试循环次数 
public testssb() { 
} 

public void test(String s){ 
long begin = System.currentTimeMillis(); 
for(int i=0;i<ttime;i++){ 
s += "add"; 
} 
long over = System.currentTimeMillis(); 
System.out.println(" 操作 "+s.getClass().getName()+" 类型使用的时间为: " + (over - begin) + " 毫秒 " ); 
} 

public void test(StringBuffer s){ 
long begin = System.currentTimeMillis(); 
for(int i=0;i<ttime;i++){ 
s.append("add"); 
} 
long over = System.currentTimeMillis(); 
System.out.println(" 操作 "+s.getClass().getName()+" 类型使用的时间为: " + (over - begin) + " 毫秒 " ); 
} 

public void test(StringBuilder s){ 
long begin = System.currentTimeMillis(); 
for(int i=0;i<ttime;i++){ 
s.append("add"); 
} 
long over = System.currentTimeMillis(); 
System.out.println(" 操作 "+s.getClass().getName()+" 类型使用的时间为: " + (over - begin) + " 毫秒 " ); 
} 

// 对 String 直接进行字符串拼接的测试 
public void test2(){ 
String s2 = "abadf"; 
long begin = System.currentTimeMillis(); 
for(int i=0;i<ttime;i++){ 
String s = s2 + s2 + s2 ; 
} 
long over = System.currentTimeMillis(); 
System.out.println(" 操作字符串对象引用相加类型使用的时间为: " + (over - begin) + " 毫秒 " ); 
} 

public void test3(){ 
long begin = System.currentTimeMillis(); 
for(int i=0;i<ttime;i++){ 
String s = "abadf" + "abadf" + "abadf" ; 
} 
long over = System.currentTimeMillis(); 
System.out.println(" 操作字符串相加使用的时间为: "+ (over - begin) + " 毫秒 " ); 
} 

public static void main(String[] args){ 
String s1 ="abc"; 
StringBuffer sb1 = new StringBuffer("abc"); 
StringBuilder sb2 = new StringBuilder("abc"); 

testssb t = new testssb(); 
t.test(s1); 
t.test(sb1); 
t.test(sb2); 
t.test2(); 
t.test3(); 
} 
}

以上代码在 NetBeans 5.0 IDE/JDK1.6 上编译通过,循环次数 ttime 为 10000 次的测试结果如下: 
操作 java.lang.String 类型使用的时间为: 4392 毫秒 
操作 java.lang.StringBuffer 类型使用的时间为: 0 毫秒 
操作 java.lang.StringBuilder 类型使用的时间为: 0 毫秒 
操作字符串对象引用相加类型使用的时间为: 15 毫秒 
操作字符串相加使用的时间为: 0 毫秒 

好像还看不出 StringBuffer 和 StringBuilder 的区别,把 ttime 加到 30000 次看看: 
操作 java.lang.String 类型使用的时间为: 53444 毫秒 
操作 java.lang.StringBuffer 类型使用的时间为: 15 毫秒 
操作 java.lang.StringBuilder 类型使用的时间为: 15 毫秒 
操作字符串对象引用相加类型使用的时间为: 31 毫秒 
操作字符串相加使用的时间为: 0 毫秒 

StringBuffer 和 StringBuilder 的性能上还是没有太大的差异,再加大到 100000 看看,这里就不加入对 String 类型的测试了,因为对 String 类型这么大数据量的测试会很慢滴…… 
操作 java.lang.StringBuffer 类型使用的时间为: 31 毫秒 
操作 java.lang.StringBuilder 类型使用的时间为: 16 毫秒 

能看出差别了,但其中有多次的测试结果居然是 StringBuffer 比 StringBuilder 快,再加大一些到 1000000 看看(应该不会当机吧?): 
操作 java.lang.StringBuffer 类型使用的时间为: 265 毫秒 
操作 java.lang.StringBuilder 类型使用的时间为: 219 毫秒 

有些少区别了,而且结果很稳定,再大点看看, ttime = 5000000 : 

······ Exception in thread "main" java.lang.OutOfMemoryError: Java heap space ······ 

呵呵,算了,不去测试了,基本来说都是在性能上都是 StringBuilder > StringBuffer > String 的了。

更多Java之String、StringBuffer、StringBuilder的区别分析相关文章请关注PHP中文网!

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。