在我們學習String類的時候,也會學習到StringBuilder和StringBuffer,但是他們之間有什么區別呢? 當然他們在具體的代碼實作上、記憶體分配上以及效率上都有著不同(我這里以JDK8為例);
它們之間的區別:
- 一、代碼實作
- String
- StringBuilder
- StringBuffer
- 二、性能效率
- String
- StringBuilder
- StringBuffer
- 三、記憶體分配
- String
- StringBuilder和StringBuffer
- 完結
一、代碼實作
String
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
/**
* Initializes a newly created {@code String} object so that it represents
* an empty character sequence. Note that use of this constructor is
* unnecessary since Strings are immutable.
*/
public String() {
this.value = "".value;
}
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
從這里我們可以看出,String的底層結構是private final char value[];所以String的值是不可變的,并且實作了Comparable介面,所以是支持比較的,
StringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the {@code capacity} argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
/**
* Constructs a string builder that contains the same characters
* as the specified {@code CharSequence}. The initial capacity of
* the string builder is {@code 16} plus the length of the
* {@code CharSequence} argument.
*
* @param seq the sequence to copy.
*/
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
從這里我們可以看出StringBuilder有一個抽象父類,他是沒有實作Comparable介面,明顯不支持比較,我們看到AbstractStringBuilder類中,value是可變的,并不像String有final修飾
AbstractStringBuilder
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
StringBuffer
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuffer(int capacity) {
super(capacity);
}
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
/**
* Constructs a string buffer that contains the same characters
* as the specified {@code CharSequence}. The initial capacity of
* the string buffer is {@code 16} plus the length of the
* {@code CharSequence} argument.
* <p>
* If the length of the specified {@code CharSequence} is
* less than or equal to zero, then an empty buffer of capacity
* {@code 16} is returned.
*
* @param seq the sequence to copy.
* @since 1.5
*/
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
而StringBuffer同樣是繼承一個抽象父類AbstractStringBuilder,它的底層結構同樣是可變的char[] value陣列也沒有實作Comparable介面,
StringBuffer和StringBuilder雖然結構是一樣的,但還是有不同點,StringBuffer多了幾樣東西,
如:
1、toStringCache它是用于執行toString()方法時,把值保存到變數中,下次繼續執行toString()方法時,可以直接從變數中取出來,變數是transient 修飾的,代表著不可序列化,
2、還有一個本質上的區別,StringBuffer的方法是synchronized修飾的,代表著是執行緒安全的,
二、性能效率
String
// Stirng
String str = "";
long oldTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str += "a";
}
System.out.printf("耗時:%d ms",(System.currentTimeMillis() - oldTime));
耗時:11114 ms
我這里使用for回圈,每回圈一次,都往str中拼接字串"a",總共回圈10萬次,執行時間達到了平均11秒,太可怕了,效率實在太慢太慢了,
StringBuilder
// StringBuilder
StringBuilder str = new StringBuilder("");
long oldTime = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
str.append("a");
}
System.out.printf("耗時:%d ms",(System.currentTimeMillis() - oldTime));
耗時:279 ms
我這里使用StringBuilder的append方法進行拼接字串,拼接1000萬次,平均執行時間只需279毫秒,這里對比String可以看出,在大量拼接字串的時候,String的劣勢很明顯,
當然StringBuilder雖然夠快,但也有他的劣勢,在多執行緒的環境下是執行緒不安全的,
StringBuffer
// StringBuffer
StringBuffer str = new StringBuffer("");
long oldTime = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
str.append("a");
}
System.out.printf("耗時:%d ms",(System.currentTimeMillis() - oldTime));
耗時:547 ms
這里同樣使用StringBuffer拼接字串,同樣1000萬次,耗時平均550毫秒,雖然它沒有StringBuilder快,但是它可以保證執行緒安全,我們前面講過StringBuffer的方法是synchronized修飾的,當然執行緒安全就代表著它性能效率會被降低了,因為我們的執行緒需要去競爭鎖,競爭到鎖的執行緒才可以執行,雖然有偏向鎖,性能難免會被降低,
三、記憶體分配
JVM記憶體區域劃分如下(自畫,如果有覺得錯的地方,可以私信我修正哈)

String
String str = “a” + “b” + “c”; //我們以這段代碼為例,我們來看看記憶體中是如何分配的

【總所周知,字串常量池在JDK8中從方法區搬到了堆中!】
我們可以看到,在使用String進行拼接不同字串的時候,每次拼接相連,都會在常量池中創建相應的字面量,這是因為String的value是final修飾的,并不能直接修改,所以會一直創建新的物件,并重新進行賦值,
StringBuilder和StringBuffer

StringBuilder和StringBuffer在記憶體分配上是一樣的,只會改自身物件中的value值,不會在堆中重復創建新的字面量進行重新賦值,但拼接完需要呼叫toString()方法轉成String型別,這時候才會在堆中創建一個"abc"字面量
完結
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/253582.html
標籤:其他
