概述
StringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix.
Prior to adding something to the StringJoiner, its sj.toString() method will, by default, return prefix + suffix. However, if the setEmptyValue method is called, the emptyValue supplied will be returned instead. This can be used, for example, when creating a string using set notation to indicate an empty set, i.e. "{}", where the prefix is "{", the suffix is "}" and nothing has been added to the StringJoiner.
StringJoiner
源碼的定義可以看出,它是 java.util
包中的一個(gè)類,被用來構(gòu)造一個(gè)由分隔符分隔的字符串,并且可以從提供的前綴字符串開頭,以提供的后綴字符串結(jié)尾。
通常我們拼接字符串都是使用 StringBuilder
或者 StringBuffer
來實(shí)現(xiàn)的。這個(gè)時(shí)候,我們可能就會(huì)有一個(gè)疑問了, StringJoiner
的價(jià)值是什么?到底為什么要到這個(gè)時(shí)候創(chuàng)造它。
源碼解析
好,我們先看一下 StringJoiner
的構(gòu)造函數(shù), StringJoiner
一共有 2 個(gè)構(gòu)造函數(shù)。構(gòu)造函數(shù)很簡(jiǎn)單,沒有什么可以多講的,就是對(duì) 分隔符、前綴和后綴字符串的初始化。
public StringJoiner(CharSequence delimiter) {
this(delimiter, "", "");
}
public StringJoiner(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
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");
// make defensive copies of arguments
this.prefix = prefix.toString();
this.delimiter = delimiter.toString();
this.suffix = suffix.toString();
}
另外 StringJoiner
有 5 個(gè)公有方法,其中比較常用的就是 add
和 toString
。我們也來看看這兩個(gè)常用方法。
public final class StringJoiner {
private String[] elts;
@Override
public String toString() {
final String[] elts = this.elts;
if (elts == null && emptyValue != null) {
return emptyValue;
}
final int size = this.size;
final int addLen = prefix.length() + suffix.length();
if (addLen == 0) {
compactElts();
return size == 0 ? "" : elts[0];
}
final String delimiter = this.delimiter;
final char[] chars = new char[len + addLen];
int k = getChars(prefix, chars, 0);
if (size > 0) {
k += getChars(elts[0], chars, k);
for (int i = 1; i < size; i++) {
k += getChars(delimiter, chars, k);
k += getChars(elts[i], chars, k);
}
}
k += getChars(suffix, chars, k);
return new String(chars);
}
public StringJoiner add(CharSequence newElement) {
final String elt = String.valueOf(newElement);
if (elts == null) {
elts = new String[8];
} else {
if (size == elts.length)
elts = Arrays.copyOf(elts, 2 * size);
len += delimiter.length();
}
len += elt.length();
elts[size++] = elt;
return this;
}
}
我們來看下 add
方法的實(shí)現(xiàn),看起來也挺簡(jiǎn)單的,就是把待拼接的字符串,放到一個(gè)字符串?dāng)?shù)組里面。toString()
方法的時(shí)候,才是真正做字符串拼接的過程。我例子中的代碼是 JDK 11
, 相比 JDK 8
中,StringJoiner
是通過 StringBuilder
來實(shí)現(xiàn)的。
既然 JDK 8
的時(shí)候,已經(jīng)使用了StringBuilder
來實(shí)現(xiàn),那么為什么還要改成 String[]
來緩存所有的待拼接的字符串。這個(gè)就要涉及到JVM底層的優(yōu)化,我們這里暫時(shí)不展開講這個(gè)問題了。
前面已經(jīng)提過既然已經(jīng)有了 StringBuilder
,為什么還要造一個(gè)StringJoiner
,它的優(yōu)勢(shì)到底在哪里,我們接著來找找原因。很快我們?cè)诖a類的注釋中找到了貓膩,在注釋中標(biāo)記了Collectors#joining
。
A StringJoiner may be employed to create formatted output from a java.util.stream.Stream using java.util.stream.Collectors.joining(CharSequence).
* @see java.util.stream.Collectors#joining(CharSequence)
* @see java.util.stream.Collectors#joining(CharSequence, CharSequence, CharSequence)
那我們就順藤摸瓜,看看 Collectors#joining
有什么跟 StringJoiner
有關(guān)聯(lián)的呢?
public static Collector< CharSequence, ?, String > joining() {
return new CollectorImpl< CharSequence, StringBuilder, String >(
StringBuilder::new, StringBuilder::append,
(r1, r2) - > { r1.append(r2); return r1; },
StringBuilder::toString, CH_NOID);
}
public static Collector< CharSequence, ?, String > joining(CharSequence delimiter) {
return joining(delimiter, "", "");
}
public static Collector< CharSequence, ?, String > joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl< >(
() - > new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add, StringJoiner::merge,
StringJoiner::toString, CH_NOID);
}
原來啊,Java 8
中 Stream
是借助了 StringJoiner
來實(shí)現(xiàn)的。這個(gè)時(shí)候,我們可能會(huì)想,為什么不使用 StringBuilder
來實(shí)現(xiàn)呢?我們可以從上面的代碼里看出,如果 使用 StringBuilder
來構(gòu)造拼接的話,在沒有前后綴的情況下,應(yīng)該還是簡(jiǎn)單的,事實(shí)上JDK 官方組織也選擇了 StringBuilder
。但是一旦涉及到拼接之類的操作,那如果還是使用 StringBuilder
的話,那就真的是太復(fù)雜了。
所以 StringJoiner
在 Java 8
的地位是 StringBuilder
所不能代替的。
總結(jié)
本文介紹了 Java 8
開始提供的字符串拼接類 StringJoiner
。JDK 8
中 StringJoiner
是通過 StringBuilder
實(shí)現(xiàn)的, 所以它的性能和 StringBuilder
差不多,它也是非線程安全的。JDK 11
中已經(jīng)對(duì)其進(jìn)行了優(yōu)化,通過 String[]
來代理 StringBuilder
。
在日常的開發(fā)過程中,我們?cè)趺催x擇字符串拼接類呢?
- 簡(jiǎn)單的字符串拼接,直接使用 + 即可。
- 在 for 循環(huán)之類的場(chǎng)景下需要字符串拼接,可以優(yōu)先考慮使用 StringBuilder 。
- 在使用 Java Stream 的場(chǎng)景下需要字符串拼接,可以優(yōu)先考慮使用 StringJoiner。
-
JAVA
+關(guān)注
關(guān)注
20文章
2989瀏覽量
109605 -
字符串
+關(guān)注
關(guān)注
1文章
590瀏覽量
22263 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4380瀏覽量
64850 -
string
+關(guān)注
關(guān)注
0文章
40瀏覽量
4910
發(fā)布評(píng)論請(qǐng)先 登錄
Java多線程的用法
新增16條設(shè)計(jì)規(guī)約!阿里巴巴Java開發(fā)手冊(cè)(詳盡版)開放下載!
java并發(fā)編程實(shí)戰(zhàn)之輔助類用法
java final關(guān)鍵字用法技巧匯總解析

Java數(shù)組的常用方法_Java:數(shù)組工具類Arrays類的常用方法的用法及代碼
Java 10 發(fā)布之后,大多數(shù)受訪者仍在使用 Java 8(82%)

Java11GC 性能基準(zhǔn)測(cè)試報(bào)告 Java8與Java11對(duì)比測(cè)試
SpringBoot正式棄用Java8 Java17將成為未來主流版本
如何正確區(qū)分Java中super函數(shù)用法
Java枚舉的特點(diǎn)及用法
Java時(shí)間類轉(zhuǎn)換方案
java 8的日期用法
this關(guān)鍵字在Java中的用法

評(píng)論