【Java基礎】Java8新特性-StringJoiner

一.StringJoiner

1.什麼是StringJoiner

StringJoiner是Java8新出的一個類,用於構造由分隔符分隔的字符串

  • 可選擇性定義每個拼接字符串的前綴以及後綴。
  • 可以避免開發人員再次通過StringBuffer或者StingBuilder拼接。

2.StringJoiner源碼解析

public final class StringJoiner {
    private final String prefix;//前綴
    private final String delimiter;//間隔符
    private final String suffix;//後綴
    private StringBuilder value;//值

    private String emptyValue;//空值

    public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");//默認前綴和後綴爲"",間隔符爲delimiter ,重載調用
    }

    public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        //間隔符,前綴和後綴判斷是否爲null,null將拋出異常
        Objects.requireNonNull(prefix, "The prefix must not be null");
        Objects.requireNonNull(delimiter, "The delimiter must not be null");
        Objects.requireNonNull(suffix, "The suffix must not be null"); 
        // 成員變量賦值
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix;//空值被設置爲只有前後綴
    }
	//設置空值,檢查是否爲null
    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }

    @Override
    public String toString() {
        if (value == null) {
            return emptyValue;//沒有值將返回空值或者後續設置的空值
        } else {
            if (suffix.equals("")) {
                return value.toString();//後綴爲""直接返回字符串,不用添加
            } else {
	            //後綴不爲"",添加後綴,然後直接返回字符串,修改長度
                int initialLength = value.length();
                String result = value.append(suffix).toString();
                // reset value to pre-append initialLength
                value.setLength(initialLength);
                return result;
            }
        }
    }
    ////初始化,先添加前綴,有了之後每次先添加間隔符,StringBuilder後續append字符串
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
	//合併StringJoiner,注意後面StringJoiner 的前綴就不要了,後面的appen進來
    public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other);
        if (other.value != null) {
            final int length = other.value.length();
            // lock the length so that we can seize the data to be appended
            // before initiate copying to avoid interference, especially when
            // merge 'this'
            StringBuilder builder = prepareBuilder();
            builder.append(other.value, other.prefix.length(), length);
        }
        return this;
    }
	//初始化,先添加前綴,有了之後每次先添加間隔符
    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

    public int length() {
        // Remember that we never actually append the suffix unless we return
        // the full (present) value or some sub-string or length of it, so that
        // we can add on more if we need to.
        //不忘添加後綴的長度
        return (value != null ? value.length() + suffix.length() :
                emptyValue.length());
    }
}

查看源碼可以發現,StringJoiner內部實際上就是使用了StringBuilder,所以拼接效率和StringBuilder幾乎是一模一樣的,但也有線程不安全的問題

3.StringJoiner常用Api

  1. 構造方法
//構造一個以指定分隔符delimiter分隔每個拼接字符串的StringJoiner
public StringJoiner(CharSequence delimiter) 
//構造一個以以指定分隔符delimiter/字符串開頭前綴prefix/字符串結尾後綴suffix 拼接字符串的StringJoiner
public StringJoiner(CharSequence delimiter,CharSequence prefix, CharSequence suffix) 

使用示例1:以分隔符拼接字符串

	@Test
    public void TestStringJoiner() {
        String[] names = {"Bob", "Alice", "Grace"};
        StringJoiner sj = new StringJoiner(", ");
        for (String name : names) {
            sj.add(name);
        }
        System.out.println(sj.toString());
    }
    //執行結果:
    //Bob, Alice, Grace

使用示例2:以分隔符+前綴+後綴拼接字符串

    @Test
    public void TestStringJoiner2() {
        String[] names = {"Bob", "Alice", "Grace"};
        StringJoiner sj = new StringJoiner(", ", "Hello ", "!");
        for (String name : names) {
            sj.add(name);
        }
        System.out.println(sj.toString());
    }
    //執行結果:
    //Hello Bob, Alice, Grace!
  1. add方法

使用示例:

    @Test
    public void TestStringJoinerAdd() {
        StringJoiner joiner = new StringJoiner("--", "[[[_", "_]]]");
        joiner.add("1");
        System.out.println("toString: " + joiner.toString());
        System.out.println("length: " + joiner.length());
    }

在這裏插入圖片描述

分析源碼

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;
}
public AbstractStringBuilder append(CharSequence s) {
        if (s == null)  return appendNull();
        if (s instanceof String)  return this.append((String)s);
        if (s instanceof AbstractStringBuilder)
                return this.append((AbstractStringBuilder)s);
        return this.append(s, 0, s.length());
}

​ 發現StringJoiner底層依舊使用的StringBuilder,第一次添加數據時,會生成StringBuilder對象,並添加 “前綴”,後續添加字符時,追加 “分隔符”,最後調用 append 方法,最底層調用 System.arraycopy方法。

public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)

src:源數組
srcPos:源數組要複製的起始位置
dest:目的數組
destPos:目的數組放置的起始位置
length:複製的長度
注意:src and dest都必須是同類型或者可以進行轉換類型的數組.

  1. toString

分析源碼

public String toString() {
        if (value == null) {
            return emptyValue;
        } else {
            if (suffix.equals("")) {
                return value.toString();
            } else {
                int initialLength = value.length();
                String result = value.append(suffix).toString();
                value.setLength(initialLength);
                return result;
            }
        }
    }
  • value == null, 返回空。
  • value不爲空,判斷 是否需要添加 後綴
  1. length

分析源碼

public int length() {
        // Remember that we never actually append the suffix unless we return
        // the full (present) value or some sub-string or length of it, so that
        // we can add on more if we need to.
        return (value != null ? value.length() + suffix.length() :
                emptyValue.length());
    }
}

value 不爲 null ,返回 value的長度 + 後綴長度(不爲null時,已經計算了value+前綴)

  1. merge
    使用示例
    @Test
    public void TestStringJoinermMerge() {
        StringJoiner joiner = new StringJoiner("--", "[[[_", "_]]]");
        joiner.add("1").add("2").add("3").add("4");

        StringJoiner joiner2 = new StringJoiner("...");
        joiner2.add("a").add("b").add("c");

        joiner.merge(joiner2);
        System.out.println(joiner.toString());
    }

在這裏插入圖片描述
分析源碼

public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other);
        if (other.value != null) {
                final int length = other.value.length();
        // lock the length so that we can seize the data to be appended
        // before initiate copying to avoid interference, especially when
        // merge 'this'
        StringBuilder builder = prepareBuilder();
                builder.append(other.value, other.prefix.length(), length);
        }
        return this;
}
// result
//[[[_1--2--3--4--a...b...c_]]]
  1. setEmptyValue 用於設置StringJoiner的默認值
    分析源碼
//設置空值,檢查是否爲null
    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }

二.String.join()

在Java8,String還提供了一個靜態方法join(),這個方法在內部使用了StringJoiner來拼接字符串,在不需要指定“開頭”和“結尾”的時候,用String.join()更方便:

String[] names = {"Bob", "Alice", "Grace"};
String s = String.join(", ", names);
//執行結果: Bob,Alice,Grace

源碼分析
String.join()方法源碼

//delimiter 分隔符
//elements 需要連接的元素
public static String join(CharSequence delimiter, CharSequence... elements) {
    //判斷是否爲null,如果爲null,拋出NullPointerException
    Objects.requireNonNull(delimiter);
    Objects.requireNonNull(elements);
    //構造一個分隔符爲delimiter的實例
    StringJoiner joiner = new StringJoiner(delimiter);
    //循環拼接
    for (CharSequence cs: elements) {
        joiner.add(cs);
    }
    return joiner.toString();
}

//delimiter 分隔符
//elements 需要連接字符串集合
public static String join(CharSequence delimiter,
            Iterable<? extends CharSequence> elements) {
                //判斷是否爲null,如果爲null,拋出NullPointerException
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        
        //構造一個分隔符爲delimiter的實例
        StringJoiner joiner = new StringJoiner(delimiter);
		
		//遍歷字符串集合elements,循環拼接到joiner中
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

源碼分析
StringJoiner構造器源碼

//1個參數構造器
public StringJoiner(CharSequence delimiter) {
    //調用3個參數構造器
    this(delimiter, "", "");
}
 
//3個參數構造器
//delimiter 分隔符
//prefix 前綴
//suffix 後綴
public StringJoiner(CharSequence delimiter,
                    CharSequence prefix,
                    CharSequence suffix) {
    //判斷是否爲null,如果爲null,拋出NullPointerException
    Objects.requireNonNull(prefix, "The prefix must not be null");
    Objects.requireNonNull(delimiter, "The delimiter must not be null");
    Objects.requireNonNull(suffix, "The suffix must not be null");
    //爲成員變量賦值
    //前綴
    this.prefix = prefix.toString();
    //分隔符
    this.delimiter = delimiter.toString();
    //後綴
    this.suffix = suffix.toString();
    this.emptyValue = this.prefix + this.suffix;
}

String.join()方法中是通過add方法拼接字符串的,add()源碼如下:

public StringJoiner add(CharSequence newElement) {
    //prepareBuilder()返回參數,調用append()方法
    prepareBuilder().append(newElement);
    return this;
}

perpareBuilder()方法源碼如下:

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

perpareBuilder()方法返回值是一個StringBuilder對象,通過調用StringBuilder.append()方法拼接字符串。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章