概述
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
包中的一個類,被用來構造一個由分隔符分隔的字符串,并且可以從提供的前綴字符串開頭,以提供的后綴字符串結尾。
通常我們拼接字符串都是使用 StringBuilder
或者 StringBuffer
來實現的。這個時候,我們可能就會有一個疑問了, StringJoiner
的價值是什么?到底為什么要到這個時候創造它。
源碼解析
好,我們先看一下 StringJoiner
的構造函數, StringJoiner
一共有 2 個構造函數。構造函數很簡單,沒有什么可以多講的,就是對 分隔符、前綴和后綴字符串的初始化。
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 個公有方法,其中比較常用的就是 add
和 toString
。我們也來看看這兩個常用方法。
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
方法的實現,看起來也挺簡單的,就是把待拼接的字符串,放到一個字符串數組里面。toString()
方法的時候,才是真正做字符串拼接的過程。我例子中的代碼是 JDK 11
, 相比 JDK 8
中,StringJoiner
是通過 StringBuilder
來實現的。
既然 JDK 8
的時候,已經使用了StringBuilder
來實現,那么為什么還要改成 String[]
來緩存所有的待拼接的字符串。這個就要涉及到JVM底層的優化,我們這里暫時不展開講這個問題了。
前面已經提過既然已經有了 StringBuilder
,為什么還要造一個StringJoiner
,它的優勢到底在哪里,我們接著來找找原因。很快我們在代碼類的注釋中找到了貓膩,在注釋中標記了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
有關聯的呢?
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
來實現的。這個時候,我們可能會想,為什么不使用 StringBuilder
來實現呢?我們可以從上面的代碼里看出,如果 使用 StringBuilder
來構造拼接的話,在沒有前后綴的情況下,應該還是簡單的,事實上JDK 官方組織也選擇了 StringBuilder
。但是一旦涉及到拼接之類的操作,那如果還是使用 StringBuilder
的話,那就真的是太復雜了。
所以 StringJoiner
在 Java 8
的地位是 StringBuilder
所不能代替的。
總結
本文介紹了 Java 8
開始提供的字符串拼接類 StringJoiner
。JDK 8
中 StringJoiner
是通過 StringBuilder
實現的, 所以它的性能和 StringBuilder
差不多,它也是非線程安全的。JDK 11
中已經對其進行了優化,通過 String[]
來代理 StringBuilder
。
在日常的開發過程中,我們怎么選擇字符串拼接類呢?
- 簡單的字符串拼接,直接使用 + 即可。
- 在 for 循環之類的場景下需要字符串拼接,可以優先考慮使用 StringBuilder 。
- 在使用 Java Stream 的場景下需要字符串拼接,可以優先考慮使用 StringJoiner。
-
JAVA
+關注
關注
19文章
2959瀏覽量
104553 -
字符串
+關注
關注
1文章
577瀏覽量
20485 -
函數
+關注
關注
3文章
4307瀏覽量
62433 -
string
+關注
關注
0文章
40瀏覽量
4719
發布評論請先 登錄
相關推薦
評論