欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

String StringBuilder StringBuffer區(qū)別以及源碼分析

 更新時(shí)間:2021年06月22日 14:27:07   作者:興趣使然的草帽路飛  
string是C++標(biāo)準(zhǔn)庫(kù)的一個(gè)重要的部分,主要用于字符串處理??梢允褂幂斎胼敵隽鞣绞街苯舆M(jìn)行string操作,同時(shí),C++的算法庫(kù)對(duì)string類也有著很好的支持,并且string類還和c語(yǔ)言的字符串之間有著良好的接口

1. String/StringBuilder/StringBuffer 區(qū)別

1.1 String

String 對(duì)象一旦創(chuàng)建之后就是不可變的,不可變的!

問(wèn)題:既然String 對(duì)象是不可變的,那么其包含的常用修改值的方法是如何實(shí)現(xiàn)的呢?

Demo

substring(int,int) 字符串截取
split(String,int) 字符串分割
toLowerCase() 字符串所有字母小寫
...

其實(shí),這些方法底層都是通過(guò)重新創(chuàng)建一個(gè)String 對(duì)象來(lái)接收變動(dòng)后的字符串,而最初的 String 對(duì)象并未發(fā)生改動(dòng)!

在這里插入圖片描述

注意點(diǎn)(重要)

我們經(jīng)常使用 String 字符串進(jìn)行 + 或者 += 操作,來(lái)改變字符串的值,這種情況比較特殊!

首先在JAVA中++= 是僅有的兩個(gè)重載過(guò)的操作符,在進(jìn)行字符串相加時(shí)候其String 對(duì)象并未發(fā)生改變,而是值發(fā)生改變了!

案例1:

String str = "Hello" + "Wrold";

使用javap 命令對(duì)其進(jìn)行反編譯后得到如下代碼:

str = new StringBuilder("Hello").append("Wrold").toString();

結(jié)論:從上面案例得出,String 字符串+ 的操作,底層其實(shí)是通過(guò) StringBuilder 執(zhí)行的!

從效率角度觸發(fā),在大部分情況下,使用 + 連接字符串并不會(huì)造成效率上的損失,而且還可以提高程序的易讀性和簡(jiǎn)潔度!

案例2:

String str = "0";
String append = "1";
for(int i = 0; i < 10000; i++){// 結(jié)果為:str = 011111.......(很多1)
    str += append;
}

這種情況下,如果反編譯的話,得到的是如下代碼:

...
for(int i = 0; i < 10000; i++){// 結(jié)果為:str = 011111.......(很多1)
    str = (new StringBuilder()).append(str).append(append).toString(); 
}    

這種情況下,由于大量 StringBuilder 創(chuàng)建在堆內(nèi)存中,肯定會(huì)造成效率的損失,所以在這種情況下建議在循環(huán)體外創(chuàng)建一個(gè) StringBuilder 對(duì)象調(diào)用 append() 方法手動(dòng)拼接!

案例3:

除了上面兩種情況,我們?cè)賮?lái)分析一種特殊情況,即:

當(dāng)+ 兩端均為字符串常量(此時(shí)指的是"xxx" 而不是final修飾的String對(duì)象)時(shí),在編譯過(guò)后會(huì)直接拼接好,例如:

System.out.println("Hello" + "World");

反編譯后變?yōu)椋?/p>

System.out.println("HelloWorld");

這樣的情況下通過(guò)+拼接字符串效率是最佳的!

1.2 StringBuilder

StringBulider 是一個(gè)可變的字符串類!可以把它看作是一個(gè)容器。

StringBuilder 可以通過(guò) toString()方法轉(zhuǎn)換成 String

String 可以通過(guò) StringBuilder 的構(gòu)造方法,轉(zhuǎn)換成 StringBuilder

eg:

String string = new StringBuffer().toString();
StringBulider stringBuilder = new StringBulider(new String());

StringBuilder 拼接字符串的效率較高,但是它不是線程安全的!

1.3 StringBuffer

StringBuffer 同樣是一個(gè)可變的字符串類!也可以被看作是一個(gè)容器。

  • StringBuffer 可以通過(guò) toString()方法轉(zhuǎn)換成 String
  • String 可以通過(guò) StringBuffer 的構(gòu)造方法,轉(zhuǎn)換成 StringBuffer

eg:

String string = new StringBuffer().toString();
StringBuffer stringBuffer = new StringBuffer(new String());

Stringbuffer拼接字符串的效率相對(duì)于 StringBuilder 較低,但是它是線程安全的!

2. String/StringBuilder/StringBuffer 源碼

2.1 String 源碼分析

2.1.1 String 類

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    ...
}
  • 首先從代碼可以看出,String 類被final 關(guān)鍵字修飾,該類不能被繼承!
  • String 還實(shí)現(xiàn)了Serializable ,Comparable<String>, CharSequence 等接口,能夠被序列化,支持字符串判等比較,且是 一個(gè) char 值的可讀序列
  • Comparable 接口有compareTo(String s)方法,CharSequence 接口有 length(),charAt(int index),subSequence(int start,int end)方法。

2.1.2 String 類的屬性

// 不可變的char數(shù)組用來(lái)存放字符串,說(shuō)明其是不可變量.
private final char value[];
// int型的變量hash用來(lái)存放計(jì)算后的哈希值.
private int hash; // 默認(rèn)為0
// 序列化版本號(hào)
private static final long serialVersionUID = -6849794470754667710L;

2.1.3 String 類的構(gòu)造函數(shù)

// 不含參數(shù)的構(gòu)造函數(shù),一般沒(méi)什么用,因?yàn)?value 是不可變量
public String() {
    this.value = "".value;// "",注意不是null
    //this.value = new char[0]; jdk1.8之前的寫法
}
// 參數(shù)為String類型的構(gòu)造函數(shù)
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}
// 參數(shù)為char數(shù)組,使用java.utils包中的Arrays類復(fù)制
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}
// 調(diào)用public String(byte bytes[], int offset, int length, String charsetName)構(gòu)造函數(shù)
public String(byte bytes[], String charsetName)
        throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
}
public String(byte bytes[], int offset, int length, String charsetName)
        throws UnsupportedEncodingException {
    if (charsetName == null)
        throw new NullPointerException("charsetName");
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(charsetName, bytes, offset, length);
}
還有其他的構(gòu)造方法,可以自己點(diǎn)入String 詳細(xì)閱讀...

2.1.4 String 類的常用方法

簡(jiǎn)單方法

// 字符串長(zhǎng)度
public int length() {
    return value.length;
}
// 字符串是否為空
public boolean isEmpty() {
    return value.length == 0;
}
// 根據(jù)下標(biāo)獲取字符數(shù)組對(duì)應(yīng)位置的字符
public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}
// 得到字節(jié)數(shù)組
public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}

重點(diǎn)方法

equals(Object anObject)

// 兩個(gè)對(duì)象之間判等
public boolean equals(Object anObject) {
    // 如果引用的是同一個(gè)對(duì)象,返回true
    if (this == anObject) {
        return true;
    }
    // 如果不是String類型的數(shù)據(jù),返回false
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
         // 如果char數(shù)組長(zhǎng)度不相等,返回false
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // 從后往前單個(gè)字符判斷,如果有不相等,返回false
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

該方法判等規(guī)則:

如果二者內(nèi)存地址相同,則為true

如果對(duì)象類型不是String 類型,則為false,如果是就繼續(xù)進(jìn)行判等

如果對(duì)象字符長(zhǎng)度不相等,則為false,如果相等就繼續(xù)進(jìn)行判等

從后往前,判斷 String 類中char 數(shù)組 value 的單個(gè)字符是否相等,有不相等則為false。如果一直相等直到第一個(gè)數(shù),則返回true

結(jié)論:根據(jù)判等規(guī)則得出,如果兩個(gè)超長(zhǎng)的字符串進(jìn)行比較,是非常費(fèi)時(shí)間的!

hashCode()

// 返回此字符串的哈希碼
public int hashCode() {
    int h = hash;
    // 如果hash沒(méi)有被計(jì)算過(guò),并且字符串不為空,則進(jìn)行hashCode計(jì)算
    if (h == 0 && value.length > 0) {
        char val[] = value;
        //計(jì)算過(guò)程
        //val[0]*31^(n-1) + val[1]*31^(n-2) + ... + val[n-1]
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

String 類重寫了hashCode方法,Object中的hashCode方法是一個(gè)Native調(diào)用。String類的hash采用多項(xiàng)式計(jì)算得來(lái),我們完全可以通過(guò)不相同的字符串得出同樣的hash,所以兩個(gè)String對(duì)象的hashCode相同,并不代表兩個(gè)String是一樣的。

那么計(jì)算hash值的時(shí)候?yàn)槭裁匆褂?1 作為基數(shù)呢?

先要明白為什么需要HashCode.每個(gè)對(duì)象根據(jù)值計(jì)算HashCode,這個(gè)code大小雖然不奢求必須唯一(因?yàn)檫@樣通常計(jì)算會(huì)非常慢),但是要盡可能的不要重復(fù),因此基數(shù)要盡量的大。另外,31N可以被編譯器優(yōu)化為左移5位后減1即31N = (N<<5)-1,有較高的性能。使用31的原因可能是為了更好的分配hash地址,并且31只占用5bits!

結(jié)論:

  • 基數(shù)要用質(zhì)數(shù):質(zhì)數(shù)的特性(只有1和自己是因子)能夠使得它和其他數(shù)相乘后得到的結(jié)果比其他方式更容易產(chǎn)成唯一性,也就是Hash Code值的沖突概率最小。
  • 選擇31是觀測(cè)哈希值分布結(jié)果后的一個(gè)選擇,不清楚原因,但的確有利(更分散,減少?zèng)_突)。

compareTo(String anotherString)

// 按字典順序比較兩個(gè)字符串,比較是基于字符串中每個(gè)字符的Unicode值
public int compareTo(String anotherString) {
    // 自身對(duì)象字符串長(zhǎng)度len1
    int len1 = value.length;
    // 被比較對(duì)象字符串長(zhǎng)度len2
    int len2 = anotherString.value.length;
    // 取兩個(gè)字符串長(zhǎng)度的最小值lim
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;
    int k = 0;
    // 從value的第一個(gè)字符開(kāi)始到最小長(zhǎng)度lim處為止,如果字符不相等,返回自身(對(duì)象不相等處字符-被比較對(duì)象不相等字符)
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    // 如果前面都相等,則返回(自身長(zhǎng)度-被比較對(duì)象長(zhǎng)度)
    return len1 - len2;
}

這個(gè)方法寫的很巧妙,先從0開(kāi)始判斷字符大小。如果兩個(gè)對(duì)象能比較字符的地方比較完了還相等,就直接返回自身長(zhǎng)度減被比較對(duì)象長(zhǎng)度,如果兩個(gè)字符串長(zhǎng)度相等,則返回的是0,巧妙地判斷了三種情況。

startsWith(String prefix,int toffset)

// 判斷此字符串的子字符串是否從指定的索引開(kāi)始,并以指定的前綴開(kāi)頭
public boolean startsWith(String prefix, int toffset) {
    char ta[] = value;
    int to = toffset;
    char pa[] = prefix.value;
    int po = 0;
    int pc = prefix.value.length;
    // Note: toffset might be near -1>>>1.
    // 如果起始地址小于0或者(起始地址+所比較對(duì)象長(zhǎng)度)大于自身對(duì)象長(zhǎng)度,返回false
    if ((toffset < 0) || (toffset > value.length - pc)) {
        return false;
    }
    // 從所比較對(duì)象的末尾開(kāi)始比較
    while (--pc >= 0) {
        if (ta[to++] != pa[po++]) {
            return false;
        }
    }
    return true;
}
// startsWith重載方法1
public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);
}
// startsWith重載方法2
public boolean endsWith(String suffix) {
    return startsWith(suffix, value.length - suffix.value.length);
}

起始比較和末尾比較都是比較經(jīng)常用得到的方法,例如在判斷一個(gè)字符串是不是http協(xié)議的,或者初步判斷一個(gè)文件是不是mp3文件,都可以采用這個(gè)方法進(jìn)行比較。

concat(String str)

// 將指定的字符串連接到該字符串的末尾
public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    int len = value.length;
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    // 注意:這里是新new一個(gè)String對(duì)象返回,而并非原來(lái)的String對(duì)象
    return new String(buf, true);
}
// 位于 java.util.Arrays 類中
public static char[] copyOf(char[] original, int newLength) {
    char[] copy = new char[newLength];
    // 調(diào)用底層c++
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

replace(char oldChar, char newChar)

// 返回一個(gè)字符串,該字符串是通過(guò)用 newChar 替換該字符串中所有出現(xiàn)的 oldChar 而產(chǎn)生的
public String replace(char oldChar, char newChar) {
    // 新舊值先對(duì)比
    if (oldChar != newChar) {
        int len = value.length;
        int i = -1;
        char[] val = value; /* avoid getfield opcode */
        // 找到舊值最開(kāi)始出現(xiàn)的位置
        while (++i < len) {
            if (val[i] == oldChar) {
                break;
            }
        }
        // 從那個(gè)位置開(kāi)始,直到末尾,用新值代替出現(xiàn)的舊值
        if (i < len) {
            char buf[] = new char[len];
            for (int j = 0; j < i; j++) {
                buf[j] = val[j];
            }
            while (i < len) {
                char c = val[i];
                buf[i] = (c == oldChar) ? newChar : c;
                i++;
            }
            // 注意:這里是新new一個(gè)String對(duì)象返回,而并非原來(lái)的String對(duì)象
            return new String(buf, true);
        }
    }
    return this;
}

trim()

// 返回值是此字符串的字符串,其中已刪除所有前導(dǎo)和尾隨的空格
public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */
    // 找到字符串前段沒(méi)有空格的位置
    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    // 找到字符串末尾沒(méi)有空格的位置
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    // 如果前后都沒(méi)有出現(xiàn)空格,返回字符串本身
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

案例分析

public static void main(String[] args) {
    String a = "a"+"b"+1;
    String b = "ab1";
    // ab1 是放在常量池(constant pool)中的
    // 所以,雖然a,b都等于 ab1,但是內(nèi)存中只有一份副本,所以 == 的結(jié)果為true
    System.out.println("a的哈希值: " + a.hashCode());
    System.out.println("b的哈希值: " +b.hashCode());
    System.out.println("a的地址: " +System.identityHashCode(a));
    System.out.println("b的地址: " +System.identityHashCode(b));
    System.out.println(a == b);
    System.out.println("---------------------------------------------------
    String a1 = new String("ab1");
    String b1 = "ab1";
    // new 方法決定了String "ab1" 被創(chuàng)建放在了內(nèi)存heap區(qū)(堆上),被a1所指向
    // b1 位于常量池 因此 == 返回了false
    System.out.println("a1的哈希值: " + a1.hashCode());
    System.out.println("b1的哈希值: " +b1.hashCode());
    System.out.println("a1的地址: " +System.identityHashCode(a1));
    System.out.println("b1的地址: " +System.identityHashCode(b1));
    System.out.println(a1 == b1);
}

輸出結(jié)果:

在這里插入圖片描述

2.2 StringBuilder 源碼分析

2.2.1 StringBuilder 類

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ...
}

首先 StringBuilder String 一樣 被 final 關(guān)鍵字修飾該類不能被繼承!

從繼承體系可以看出, StringBuilder 繼承了AbstractStringBuilder類,該類中包含了可變字符串的相關(guān)操作方法:append()、insert()、delete()、replace()、charAt()等等。**StringBuffer 和 StringBuilder ** 均繼承該類!

比如在 StringBuilder 中的 append(String str) 方法(后文還會(huì)講到):

// java.lang.StringBuilder 類中
@Override
public StringBuilder append(String str) {
    // 調(diào)用父類 AbstractStringBuilder 的append(String str) 方法
    super.append(str);
    return this;
}
// java.lang.AbstractStringBuilder 類中(有興趣可以自己點(diǎn)進(jìn)去閱讀該類的源碼,還是很有收獲的,這里只作簡(jiǎn)述?。?
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    // 其成員屬性value[]數(shù)組擴(kuò)容,類似于ArrayList 的擴(kuò)容
    // 擴(kuò)容算法:int newCapacity = (value.length << 1) + 2;
    // 注意容量有上限:MAX_ARRAY_SIZE
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

StringBuilder 還實(shí)現(xiàn)了Serializable 接口 和 CharSequence 接口,說(shuō)明該類的對(duì)象可以被序列化,并附帶了對(duì)字符序列進(jìn)行只讀訪問(wèn)的方法,比如:length()、charAt()、subSequence()、toString()方法等。

2.2.2 StringBuilder 類的屬性

/**
 * 用于字符存儲(chǔ)的char數(shù)組,value是一個(gè)動(dòng)態(tài)的數(shù)組,當(dāng)存儲(chǔ)容量不足時(shí),會(huì)對(duì)它進(jìn)行擴(kuò)容.
 */
char[] value;
/**
 * 表示value數(shù)組中已存儲(chǔ)的字符數(shù).
 */
int count;

關(guān)鍵點(diǎn)

該數(shù)組和 Sting 中的value數(shù)組不同,其未被 final 關(guān)鍵字修飾,其值是可以被修改的!

這兩個(gè)成員屬性并不位于java.lang.StringBuilder 中,而位于其父類 java.lang.AbstractStringBuilder 中,同樣的StringBuffer 中的這兩個(gè)屬性也是位于該父類中!

2.2.3 StringBuilder 類中的構(gòu)造方法

StringBuilder類提供了4個(gè)構(gòu)造方法。構(gòu)造方法主要完成了對(duì)value數(shù)組的初始化。

// 位于父類StringBuilder 中
// 空參StringBuilder 默認(rèn)char數(shù)組容量為16
public StringBuilder() {
    super(16);
}
// 由參數(shù)傳入容量,構(gòu)建StingBuilder
public StringBuilder(int capacity) {
    super(capacity);
}
// 根據(jù)字符串參數(shù)的長(zhǎng)度,構(gòu)建容量為 16 +  字符串長(zhǎng)度的StringBuilder
public StringBuilder(String str) {
    super(str.length() + 16);
    // 調(diào)用父類append 方法添加字符串str
    append(str);
}
// 接收一個(gè)CharSequence對(duì)象作為參數(shù),設(shè)置了value數(shù)組的初始容量為CharSequence對(duì)象的長(zhǎng)度+16
// 并把CharSequence對(duì)象中的字符添加到value數(shù)組中
public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    // 調(diào)用父類append 方法添加字符串seq
    append(seq);
}
// 位于父類AbstractStringBuilder中
// 創(chuàng)建指定容量的AbstractStringBuilder
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

總結(jié)

其實(shí)String 、StingBuilder 、StirngBuffer 也類似于容器,因?yàn)槠涞讓泳诰S護(hù)一個(gè) char 類型的數(shù)組

2.2.4 StringBuilder 類中的方法 append(Object obj)方法

append 方法的重載方法參數(shù)有多種,比如String int 等等,原理類似,這里只舉String 和 Boolean 為例子:

// 位于父類StringBuilder 中
@Override
public StringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}
// 位于父類StringBuilder 中
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
// 位于父類StringBuilder 中
@Override
public StringBuilder append(boolean b) {
    super.append(b);
    return this;
}
...
// 位于父類AbstractStringBuilder中
// 添加String 類型
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
// 位于父類AbstractStringBuilder中
// 添加Boolean 類型
public AbstractStringBuilder append(boolean b) {
    if (b) {
        ensureCapacityInternal(count + 4);
        value[count++] = 't';
        value[count++] = 'r';
        value[count++] = 'u';
        value[count++] = 'e';
    } else {
        ensureCapacityInternal(count + 5);
        value[count++] = 'f';
        value[count++] = 'a';
        value[count++] = 'l';
        value[count++] = 's';
        value[count++] = 'e';
    }
    return this;
}

append()方法將指定參數(shù)類型的字符串表示形式追加到字符序列的末尾。

StringBuilder 類提供了一系列的append()方法,它可以接受boolean、char、char[]、CharSequence、double、float、int、long、Object、String、StringBuffer這些類型的參數(shù)。

這些方法最終都調(diào)用了父類AbstractStringBuilder類中對(duì)應(yīng)的方法。最后,append()方法返回了StringBuilder對(duì)象自身,以便用戶可以鏈?zhǔn)秸{(diào)用StringBuilder類中的方法。

AbstractStringBuilder類的各個(gè)append()方法大同小異。append()方法在追加字符到value數(shù)組中之前都會(huì)調(diào)用ensureCapacityInternal()方法來(lái)確保value數(shù)組有足夠的容量,然后才把字符追加到value數(shù)組中。

// 位于父類AbstractStringBuilder中
// 判斷value數(shù)組的容量是否足夠,如果不夠,那么調(diào)用newCapacity 方法進(jìn)行擴(kuò)容
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}
// 位于父類AbstractStringBuilder中
// 返回新數(shù)組容量
private int newCapacity(int minCapacity) {
    // 將數(shù)組容量擴(kuò)大到原數(shù)組容量的2倍+2
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}
// 位于父類AbstractStringBuilder中
// 數(shù)組最大容量
private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

ensureCapacityInternal()方法判斷value數(shù)組的容量是否足夠,如果不夠,那么調(diào)用newCapacity()方法進(jìn)行擴(kuò)容。

newCapacity()方法默認(rèn)情況下將數(shù)組容量擴(kuò)大到原數(shù)組容量的2倍+2。數(shù)組的容量最大只能擴(kuò)容到Integer.MAX_VALUE。

最后,調(diào)用Arrays類的copyOf()靜態(tài)方法來(lái)創(chuàng)建一個(gè)新數(shù)組和拷貝原數(shù)據(jù)到新數(shù)組,并將value指向新數(shù)組。

delete()方法

// 位于父類StringBuilder 中
@Override
public StringBuilder delete(int start, int end) {
    super.delete(start, end);
    return this;
}
// 位于父類AbstractStringBuilder中
public AbstractStringBuilder delete(int start, int end) {
    if (start < 0)
        throw new StringIndexOutOfBoundsException(start);
    if (end > count)
        end = count;
    if (start > end)
        throw new StringIndexOutOfBoundsException();
    int len = end - start;
    if (len > 0) {
        System.arraycopy(value, start+len, value, start, count-end);
        count -= len;
    }
    return this;
}

delete()方法刪除指定位置的字符。刪除的字符從指定的start位置開(kāi)始,直到end-1位置。delete()方法也調(diào)用了父類AbstractStringBuilder類中對(duì)應(yīng)的方法。

delete()方法首先檢查參數(shù)的合法性。當(dāng)end大于value數(shù)組中已存儲(chǔ)的字符數(shù)count時(shí),endcount值。最后,當(dāng)需要?jiǎng)h除的字符數(shù)大于1的時(shí)候,調(diào)用System類的arraycopy()靜態(tài)方法進(jìn)行數(shù)組拷貝完成刪除字符的操作,并更新count的值。

replace()方法

// 位于父類StringBuilder 中
@Override
public StringBuilder replace(int start, int end, String str) {
    super.replace(start, end, str);
    return this;
}
// 位于父類AbstractStringBuilder中
public AbstractStringBuilder replace(int start, int end, String str) {
    if (start < 0)
        throw new StringIndexOutOfBoundsException(start);
    if (start > count)
        throw new StringIndexOutOfBoundsException("start > length()");
    if (start > end)
        throw new StringIndexOutOfBoundsException("start > end");
    if (end > count)
        end = count;
    int len = str.length();
    int newCount = count + len - (end - start);
    ensureCapacityInternal(newCount);
    System.arraycopy(value, end, value, start + len, count - end);
    str.getChars(value, start);
    count = newCount;
    return this;
}
// 位于java.lang.String 中
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, 0, dst, dstBegin, value.length);
}

replace()方法將指定位置的字符替換成指定字符串中的字符。替換的字符從指定的start位置開(kāi)始,直到end-1位置。

replace()方法也調(diào)用了父類AbstractStringBuilder類中對(duì)應(yīng)的方法。

replace()方法首先檢查參數(shù)的合法性。當(dāng)end大于value數(shù)組中已存儲(chǔ)的字符數(shù)count時(shí),endcount值。然后調(diào)用ensureCapacityInternal()方法確保value數(shù)組有足夠的容量。接著調(diào)用System類的arraycopy()靜態(tài)方法進(jìn)行數(shù)組拷貝,主要的作用是從start位置開(kāi)始空出替換的字符串長(zhǎng)度len大小的位置。最后,調(diào)用String類的getChars()方法將替換的字符串中的字符拷貝到value數(shù)組中。這樣就完成了替換字符的操作。

toString()方法

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

toString()方法返回一個(gè)表示該字符序列的字符串,將SrpingBuilder 轉(zhuǎn)換成了 String 類型。

總結(jié)

StringBuilder類使用了一個(gè)char數(shù)組來(lái)存儲(chǔ)字符。該數(shù)組是一個(gè)動(dòng)態(tài)的數(shù)組,當(dāng)存儲(chǔ)容量不足時(shí),會(huì)對(duì)它進(jìn)行擴(kuò)容。

StringBuilder對(duì)象是一個(gè)可變的字符序列。

StringBuilder類是非線程安全的。

2.3 StringBuffer 源碼分析

2.3.1 StringBuffer 類

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ...
}

從繼承體系中得出,StingBuffer StringBuilder 繼承了相同父類,實(shí)現(xiàn)了相同的接口,且都被 final 關(guān)鍵字修飾不可被繼承,所以不再重復(fù)贅述!

2.3.2 StringBuffer 類的屬性

/**
 * 用于字符存儲(chǔ)的char數(shù)組,value是一個(gè)動(dòng)態(tài)的數(shù)組,當(dāng)存儲(chǔ)容量不足時(shí),會(huì)對(duì)它進(jìn)行擴(kuò)容.
 */
char[] value;
/**
 * 表示value數(shù)組中已存儲(chǔ)的字符數(shù).
 */
int count;

StringBuffer StringBuilder 以及 String 一樣本質(zhì)上都維護(hù)了一個(gè)字符數(shù)組!且,StringBuffer StringBuilder 的字符數(shù)組沒(méi)有 final 修飾可以被重新賦值,而String 中的字符數(shù)組加了final 不可變(常量)!

2.3.3 StringBuffer 類的構(gòu)造函數(shù)

public StringBuffer() {
    super(16);
}
public StringBuffer(int capacity) {
    super(capacity);
}
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}
...

在構(gòu)造函數(shù)上 StringBufferStringBuilder 沒(méi)區(qū)別,不再重復(fù)贅述!

2.3.4 StringBuffer 的方法

StringBuffer的主要操作有append、insert等,這些操作都是在value上進(jìn)行的,而不是像String一樣每次操作都要new一個(gè)新的String對(duì)象,因此,StringBuffer在效率上要高于String。有了append、insert等操作,value的大小就會(huì)改變,那么StringBuffer是如何操作容量的改變的呢?

StringBuffer 的擴(kuò)容

StringBuffer有個(gè)繼承自AbstractStringBuilder類的ensureCapacity的方法:

// 位于StringBuffer 中
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
    super.ensureCapacity(minimumCapacity);
}
// 位于父類中
public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0)
        ensureCapacityInternal(minimumCapacity);
}

StringBuffer對(duì)其進(jìn)行了重寫,直接調(diào)用父類的expandCapacity方法。這個(gè)方法用來(lái)保證value的長(zhǎng)度大于給定的參數(shù)minimumCapacity,在父類的ensureCapacityInternal方法中這樣操作:

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
  	  }
}
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

最后得到的新value數(shù)組大小是max(value.length*2+2,minimumCapacity),上面代碼中的第二個(gè)判斷是為了防止newCapacity溢出。

setLength方法

該方法用于直接設(shè)置字符數(shù)組元素的count數(shù)量:

// 位于StringBuffer 中
@Override
public synchronized void setLength(int newLength) {
    toStringCache = null;
    super.setLength(newLength);
}
// 為于父類中
public void setLength(int newLength) {
    if (newLength < 0)
        throw new StringIndexOutOfBoundsException(newLength)
    ensureCapacityInternal(newLength);
    if (count < newLength) {
        Arrays.fill(value, count, newLength, '\0');
    }
    count = newLength;
}

從代碼中可以看出,如果newLength大于count,那么就會(huì)在后面添加'\0'補(bǔ)充;如果小于count,就直接使count = newLength。

appen/insert 方法

StringBuffer中的每一個(gè)appendinsert函數(shù)都會(huì)調(diào)用父類的函數(shù):

@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}
@Override
public synchronized StringBuffer insert(int index, char[] str, int offset, int len) {
    toStringCache = null;
    super.insert(index, str, offset, len);
    return this;
}

而在父類中,這些函數(shù)都會(huì)首先保證value的大小夠存儲(chǔ)要添加的內(nèi)容:

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
public AbstractStringBuilder insert(int index, char[] str, int offset, int len){
    if ((index < 0) || (index > length()))
        throw new StringIndexOutOfBoundsException(index);
    if ((offset < 0) || (len < 0) || (offset > str.length - len))
        throw new StringIndexOutOfBoundsException(
            "offset " + offset + ", len " + len + ", str.length "
            + str.length);
    ensureCapacityInternal(count + len);
    System.arraycopy(value, index, value, index + len, count - index);
    System.arraycopy(str, offset, value, index, len);
    count += len;
    return this;
}

父類中通過(guò)ensureCapacityInternal的函數(shù)保證大小,函數(shù)如下:

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0)
        expandCapacity(minimumCapacity);
}

如果空間不夠,最終也是調(diào)用expandCapacity方法。這樣就保證了隨著操作value的空間夠用。

toString 方法

StringBuffer可以通過(guò)toString方法轉(zhuǎn)換為String,它有一個(gè)私有的字段toStringCache!

這個(gè)字段表示是一個(gè)緩存,用來(lái)保存上一次調(diào)用toString的結(jié)果,如果value的字符串序列發(fā)生改變,就會(huì)將它清空

首先判斷toStringCache是否為null,如果是,則先將value復(fù)制到緩存里,然后使用toStringCache 為參數(shù),去new 一個(gè) String 對(duì)象并返回!

總結(jié)

可以看出,StringBuffer 和 StringBuilder 在添加append()方法上的區(qū)別是,前者加上了synchronized 鎖,因此其是線程安全的,后者是非線程安全的!

而判斷是否擴(kuò)容以及擴(kuò)容方式,StringBuffer StringBuilder 二者沒(méi)什么區(qū)別!

總結(jié)

本篇文章的內(nèi)容就到這了,希望大家可以喜歡,也希望大家可以多多關(guān)注腳本之家的其他精彩內(nèi)容!

相關(guān)文章

  • Java動(dòng)態(tài)規(guī)劃之硬幣找零問(wèn)題實(shí)現(xiàn)示例

    Java動(dòng)態(tài)規(guī)劃之硬幣找零問(wèn)題實(shí)現(xiàn)示例

    本文主要介紹了Java動(dòng)態(tài)規(guī)劃之硬幣找零問(wèn)題實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 一文徹底弄懂零拷貝原理以及java實(shí)現(xiàn)

    一文徹底弄懂零拷貝原理以及java實(shí)現(xiàn)

    零拷貝(英語(yǔ): Zero-copy) 技術(shù)是指計(jì)算機(jī)執(zhí)行操作時(shí),CPU不需要先將數(shù)據(jù)從某處內(nèi)存復(fù)制到另一個(gè)特定區(qū)域,下面這篇文章主要給大家介紹了關(guān)于零拷貝原理以及java實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • Java Swagger使用教程

    Java Swagger使用教程

    Swagger是一個(gè)規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化 Restful 風(fēng)格的 Web 服務(wù)??傮w目標(biāo)是使客戶端和文件系統(tǒng)作為服務(wù)器以同樣的速度來(lái)更新。文件的方法、參數(shù)和模型緊密集成到服務(wù)器端的代碼,允許API來(lái)始終保持同步
    2022-07-07
  • springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式

    springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式

    這篇文章主要介紹了springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 詳解Java中方法next()和nextLine()的區(qū)別與易錯(cuò)點(diǎn)

    詳解Java中方法next()和nextLine()的區(qū)別與易錯(cuò)點(diǎn)

    這篇文章主要介紹了詳解Java中方法next()和nextLine()的區(qū)別與易錯(cuò)點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Mybatis-Plus實(shí)現(xiàn)自定義SQL具體方法

    Mybatis-Plus實(shí)現(xiàn)自定義SQL具體方法

    Mybatis-Plus是Mybatis的一個(gè)增強(qiáng)工具,它可以優(yōu)化我們的開(kāi)發(fā)效率,這篇文章主要介紹了Mybatis-Plus實(shí)現(xiàn)自定義SQL,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • Spring Boot整合Spring Data Jpa代碼實(shí)例

    Spring Boot整合Spring Data Jpa代碼實(shí)例

    這篇文章主要介紹了Spring Boot整合Spring Data Jpa代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 解決maven中只有Lifecycle而Dependencies和Plugins消失的問(wèn)題

    解決maven中只有Lifecycle而Dependencies和Plugins消失的問(wèn)題

    這篇文章主要介紹了maven中只有Lifecycle而Dependencies和Plugins消失的問(wèn)題及解決方法,本文通過(guò)圖文的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-07-07
  • JVM性能調(diào)優(yōu)實(shí)戰(zhàn):讓你的IntelliJ Idea縱享絲滑

    JVM性能調(diào)優(yōu)實(shí)戰(zhàn):讓你的IntelliJ Idea縱享絲滑

    這篇文章主要介紹了JVM性能調(diào)優(yōu)實(shí)戰(zhàn):讓你的IntelliJ Idea縱享絲滑的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • mybatis中<choose>標(biāo)簽的用法說(shuō)明

    mybatis中<choose>標(biāo)簽的用法說(shuō)明

    這篇文章主要介紹了mybatis中<choose>標(biāo)簽的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評(píng)論