博客 / 詳情

返回

關於java中的字符串拼接方式

前置知識

  1. String是java中的不可變類,一旦被實例化就無法再修改

    不可變類的實例一旦創建,其成員變量的值就不能被修改。這樣設計可以緩存 hashcode、使用更加便利以及更加安全等。
  2. java不支持運算符重載

    運算符重載:在計算機程序設計中,運算符重載(英語:operator overloading)是多態的一種。運算符重載,就是對已有的運算符重新進行定義,賦予其另一種功能,以適應不同的數據類型。
    語法糖:語法糖(Syntactic sugar),也譯為糖衣語法,是由英國計算機科學家彼得·蘭丁發明的一個術語,指計算機語言中添加的某種語法,這種語法對語言的功能沒有影響,但是更方便程序員使用。語法糖讓程序更加簡潔,有更高的可讀性。

常見的字符串拼接方法有 使用符號‘+’拼接、使用String類中的concat方法拼接、使用StringBuffer拼接、使用StringBuilder拼接、StringUtils.join

使用符號‘+’拼接

使用+拼接字符串,其實只是Java提供的一個語法糖,其實現原理是StringBuilder.append

// 使用符號‘+’拼接字符串
String hollis = wechat + "," + introduce;

// 上面代碼的反編譯結果
String hollis = (new StringBuilder()).append(wechat).append(",").append(introduce).toString();

從反編譯後的代碼,使用+拼接字符串每次都是new了一個StringBuilder,然後再把String轉成StringBuilder,再進行append。

如果在for循環中使用+拼接字符串,會頻繁的new一個對象,不僅僅會耗費時間,還會造成內存資源的浪費。

所以,根據阿里巴巴Java開發手冊建議:循環體內,字符串的連接方式,使用 StringBuilder 的 append 方法進行擴展,而不要使用+。

使用String類中的concat方法拼接

用法

String hollis = "wechat".concat(",").concat("introduce");

String類中concat方法的源碼

public String concat(String str) {
    if (str.isEmpty()) {
        return this;
    }
    int len = value.length;
    int otherLen = str.length();
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}

可以看到,concat方法首先創建了一個字符數組,長度是已有字符串和待拼接字符串的長度之和,再把兩個字符串的值複製到新的字符數組中,並使用這個字符數組創建一個新的 String 對象並返回。

通過源碼我們也可以看到,經過 concat 方法,其實是new了一個新的 String,這也就呼應到前面我們説的字符串的不變性問題上了。

使用StringBuffer和StringBuilder拼接

StringBuffer和StringBuilder都繼承了AbstractStringBuilder類,在AbstractStringBuilder類中定義了一個字符數組 char[] value ,與String類中不同的是,它沒有final修飾符,所以是可以被修改的
StringBuffer 和 StringBuilder 最大的區別就是 StringBuffer是線程安全的,StringBuffer使用synchronized進行聲明,重寫了AbstractStringBuilder類中的部分方法,

@Override
    public synchronized int length() {
        return count;
    }

StringUtils.join

Apache(version3.8)的StringUtils.join的源碼

public static String join(final char[] array, final char separator, final int startIndex, final int endIndex) {
    if (array == null) {
        return null;
    }
    final int noOfItems = endIndex - startIndex;
    if (noOfItems <= 0) {
        return EMPTY;
    }
    final StringBuilder buf = newStringBuilder(noOfItems);
    for (int i = startIndex; i < endIndex; i++) {
        if (i > startIndex) {
            buf.append(separator);
        }
        buf.append(array[i]);
    }
    return buf.toString();
}

可以看到,StringUtils.join是通過StringBuffer實現的,其最主要的功能是 將數組或集合以某拼接符拼接到一起形成新的字符串

StringJoiner

StringJoiner 是 java.util包中的一個類,用於構造一個由分隔符分隔的字符序列(可選),並且可以從提供的前綴開始並以提供的後綴結尾。

用法

public class StringJoinerTest {

    public static void main(String[] args) {
        StringJoiner sj = new StringJoiner("Hollis");    // Hollis是分隔符

        sj.add("hollischuang");
        sj.add("Java乾貨");
        System.out.println(sj.toString());

        StringJoiner sj1 = new StringJoiner(":","[","]");    // StringJoiner(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

        sj1.add("Hollis").add("hollischuang").add("Java乾貨");
        System.out.println(sj1.toString());
    }
}

// 以上代碼返回結果
// hollischuangHollisJava乾貨
// [Hollis:hollischuang:Java乾貨]

需要注意的是,當我們StringJoiner(CharSequence delimiter)初始化一個StringJoiner的時候,這個delimiter其實是分隔符,並不是可變字符串的初始值。

根據StringJoiner.add方法的源碼,可以看到,其實現原理也是依賴的StringBuilder

public StringJoiner add(CharSequence newElement) {
    prepareBuilder().append(newElement);
    return this;
}

private StringBuilder prepareBuilder() {
    if (value != null) {
        value.append(delimiter);
    } else {
        value = new StringBuilder().append(prefix);
    }
    return value;
}

list.stream().collect(Collectors.joining(","))也是藉助StringJoiner類實現的列表拼接字符串,但是使用StringJoiner類可以方便地增加前綴和後綴,適用於字符串拼接有前、後綴的場景。


如果日常開發中,需要進行字符串拼接,如何選擇?

  • 如果只是簡單的字符串拼接,不是在循環體中進行字符串拼接的話,直接使用+就好了
  • 如果是在 for 循環中進行字符串拼接,考慮使用StringBuilder和StringBuffer
  • 如果在併發場景中進行字符串拼接的話,要使用StringBuffer來代替StringBuilder
  • 如果是通過一個List進行字符串拼接,則考慮使用StringUtils.join和StringJoiner

參考文章

關於 Java 字符串拼接的幾種方式以及性能比較

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.