String、StringBuilder文字列の接続効率とStringBuilderとStringBufferスレッドのセキュリティの比較
8184 ワード
一、文字列接続の効率問題
Stringを使用して文字列を接続する場合、なぜ遅いですか?
小知識点
JAvaで配列を初期化すると、その配列が占めるメモリ空間、配列長は可変ではありません.
文字列を作成し、文字列オブジェクトにメモリ空間を割り当てると、一定の時間(CPU)と空間(メモリ)の代価がかかり、最も基礎的なデータ型として文字列が大量に頻繁に作成され、プログラムの性能に大きく影響します.
無駄な中間オブジェクトが多すぎる
文字列を接続するたびに新しいStringオブジェクトが作成され、接合回数が増えるにつれてこのオブジェクトはますます大きくなります.例えば、100回の接合を行うには、100個のStringオブジェクトを作成する必要があります.
StringBuilderは接続時になぜ効率が高いのですか?
文字配列の拡張メカニズム:
元のStringBuilderオブジェクトに文字列を追加する場合:
1.追加対象strがnullの場合は'null'文字を追加
2.拡張操作が必要か確認する
3.str.getChars()strをvalueの末尾に追加
効率が高い理由拡張メカニズムは、拡張条件
コード#コード#
二、StringBuilderとString Bufferのスレッド安全比較
StringBufferのスレッドセキュリティの検証
スレッドが安全でない理由
テストコード
三、結論
1.Stringは固定長の文字列であり、StringBuilderとStringBufferは長くなる文字列である.2.StringBufferはスレッドが安全で、StringBuilderは非スレッドが安全です.3.StringBuilderとStringBufferのデフォルトの初期容量は16で、文字列の長さを事前に見積もることができ、拡張による追加のオーバーヘッドをさらに削減することができます.
Stringを使用して文字列を接続する場合、なぜ遅いですか?
小知識点
JAvaで配列を初期化すると、その配列が占めるメモリ空間、配列長は可変ではありません.
文字列を作成し、文字列オブジェクトにメモリ空間を割り当てると、一定の時間(CPU)と空間(メモリ)の代価がかかり、最も基礎的なデータ型として文字列が大量に頻繁に作成され、プログラムの性能に大きく影響します.
無駄な中間オブジェクトが多すぎる
文字列を接続するたびに新しいStringオブジェクトが作成され、接合回数が増えるにつれてこのオブジェクトはますます大きくなります.例えば、100回の接合を行うには、100個のStringオブジェクトを作成する必要があります.
StringBuilderは接続時になぜ効率が高いのですか?
文字配列の拡張メカニズム:
private void ensureCapacityInternal(int minimumCapacity) {
// minimumCapacity
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// newCapacity
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
//
if (newCapacity - minCapacity < 0) {
// minimumCapacity
newCapacity = minCapacity;
}
// newCapacity ,newCapacity MAX_ARRAY_SIZE 。
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private int hugeCapacity(int minCapacity) {
// minCapacity Integer.MAX_VALUE
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
// minCapacity MAX_ARRAY_SIZE Integer.MAX_VALUE , minCapacity, MAX_ARRAY_SIZE 。
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
元のStringBuilderオブジェクトに文字列を追加する場合:
1.追加対象strがnullの場合は'null'文字を追加
2.拡張操作が必要か確認する
2.1 minimumCapacity , 。
2.2 newCapacity,newCapacity = (value.length * 2) + 2。
2.3 , minimumCapacity。
2.4 newCapacity ,newCapacity MAX_ARRAY_SIZE 。 , minCapacity Integer.MAX_VALUE , minCapacity MAX_ARRAY_SIZE Integer.MAX_VALUE , minCapacity, MAX_ARRAY_SIZE 。
3.str.getChars()strをvalueの末尾に追加
効率が高い理由
minimumCapacity - value.length > 0
を満たす場合にのみ拡張を行い、新しい配列を生成することを保証しているため、ほとんどの場合、元の配列を操作し、不要なchar[]オブジェクトの発生を回避し、システムリソースのオーバーヘッドを節約する.コード#コード#
/**
*
*
* @Author: lingyejun
* @Date: 2019/8/17
* @Describe:
* @Modified By:
*/
public class LinkCompare {
/**
*
*
* @param times
*/
public static void linkByString(int times) {
Long startTime = System.currentTimeMillis();
String initStr = "";
for (int i = 0; i < times; i++) {
initStr = initStr + i;
}
Long endTime = System.currentTimeMillis();
System.out.println("String " + times + " :" + (endTime - startTime) + "ms");
}
/**
* StringBuilder
*
* @param times
*/
public static void linkByStringBuilder(int times) {
Long startTime = System.currentTimeMillis();
StringBuilder initStr = new StringBuilder();
for (int i = 0; i < times; i++) {
initStr.append(i);
}
Long endTime = System.currentTimeMillis();
System.out.println("StringBuilder " + times + " :" + (endTime - startTime) + "ms");
}
/**
* StringBuffer
*
* @param times
*/
public static void linkByStringBuffer(int times) {
Long startTime = System.currentTimeMillis();
StringBuffer initStr = new StringBuffer();
for (int i = 0; i < times; i++) {
initStr.append(i);
}
Long endTime = System.currentTimeMillis();
System.out.println("StringBuffer " + times + " :" + (endTime - startTime) + "ms");
}
public static void main(String[] args) {
// 100000000
linkByStringBuilder(40000);
//-XX:+PrintGCDetails
//linkByString(40000);
}
}
二、StringBuilderとString Bufferのスレッド安全比較
StringBufferのスレッドセキュリティの検証
スレッドが安全でない理由
public StringBuilder append(String str) {
super.append(str);
return this;
}
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
テストコード
import java.util.ArrayList;
import java.util.List;
/**
* StringBuilder StringBuffer
*
* @Author: lingyejun
* @Date: 2019/8/17
* @Describe:
* @Modified By:
*/
public class SecurityCompare {
public void stringBuilderTest() {
// StringBuilder
StringBuilder stringBuilder = new StringBuilder();
// joinList
List joinList = new ArrayList<>();
//
for (int i = 0; i < 1000; i++) {
StringBuilderThread sbt = new StringBuilderThread(stringBuilder);
sbt.start();
joinList.add(sbt);
}
// append
for (StringBuilderThread thread : joinList) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//
System.out.println("StringBuilder append : " + stringBuilder.length());
}
public void stringBufferTest() {
// StringBuffer
StringBuffer stringBuffer = new StringBuffer();
// joinList
List joinList = new ArrayList<>();
//
for (int i = 0; i < 1000; i++) {
StringBufferThread sbf = new StringBufferThread(stringBuffer);
sbf.start();
joinList.add(sbf);
}
// append
for (StringBufferThread thread : joinList) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//
System.out.println("StringBuffer append : " + stringBuffer.length());
}
public static void main(String[] args) {
SecurityCompare securityCompare = new SecurityCompare();
securityCompare.stringBuilderTest();
securityCompare.stringBufferTest();
}
public static class StringBuilderThread extends Thread {
private StringBuilder stringBuilder;
public StringBuilderThread(StringBuilder stringBuilder) {
this.stringBuilder = stringBuilder;
}
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
stringBuilder.append("a");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class StringBufferThread extends Thread {
private StringBuffer stringBuffer;
public StringBufferThread(StringBuffer stringBuffer) {
this.stringBuffer = stringBuffer;
}
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
stringBuffer.append("a");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三、結論
1.Stringは固定長の文字列であり、StringBuilderとStringBufferは長くなる文字列である.2.StringBufferはスレッドが安全で、StringBuilderは非スレッドが安全です.3.StringBuilderとStringBufferのデフォルトの初期容量は16で、文字列の長さを事前に見積もることができ、拡張による追加のオーバーヘッドをさらに削減することができます.