一.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
- 構造方法
//構造一個以指定分隔符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!
- 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都必須是同類型或者可以進行轉換類型的數組.
- 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不爲空,判斷 是否需要添加 後綴
- 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+前綴)
- 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_]]]
- 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()
方法拼接字符串。