java中對數組進行初始化後,該數組所佔的內存空間、數組長度都是不可變的。java
建立一個字符串,爲字符串對象分配內存空間,會耗費掉必定的時間(CPU)與空間(內存)代價,做爲最基礎的數據類型,大量頻繁的建立字符串,極大程度地影響程序的性能。數組
每次鏈接字符串時都會建立一個新的String對象,隨着拼接次數的增多,這個對象會愈來愈大。 如,進行100次拼接須要建立100個String對象纔可以達到目的。a安全
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對象中追加字符串時:bash
1.追加對象str爲null時追加'null'字符併發
2.確認是否須要進行擴容操做app
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的末尾ide
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);
}
}
複製代碼
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<StringBuilderThread> 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<StringBufferThread> 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,能夠提早預估好字符串的長度,進一步減小擴容帶來的額外開銷。性能