前言
日常工作中拼接字符串肯定是我们少不了的操作,最近工作中正好用到了,然后想起前几天看到的Java 8的一个新类:StringJoiner类,所以今天正好来看一看它的源码。
正文
用法简介
这个类的用法其实很简单,我们可以看到源码中给出的例子:
StringJoiner sj = new StringJoiner(":", "[", "]");
sj.add("George").add("Sally").add("Fred");
String desiredString = sj.toString();
我们把这个字符串最终打印出来:
[George:Sally:Fred]
所以基本上能看出一点用法了: 通过带参构造器构造出一个StringJoiner的对象,调用add方法开始拼接字符串,构造器中的参数会决定拼接的字符串的前后缀以及中间的连接符,最终调用toString方法转换为String对象。
一张图总览下这个类里都有些什么,后面逐一解释。

参数
StringJoiner总共有五个参数:
private final String prefix; //前缀
private final String delimiter; //分隔符
private final String suffix; //后缀
private StringBuilder value;
private String emptyValue;
后两个参数很有意思
private StringBuilder value;
其实是Joiner的底层,说到底StringJoiner还是调用的StringBuiler方法,只是这层封装里加上了有关于前缀,后缀和连接符的操作,让我们可以方便一些。
private String emptyValue;
emptyValue 你可以把它看作是当你的StringJoiner对象没有进行任何add的操作时,调用toString() 方法会return 这个字符串而不是空。具体的用法后面看到setEmptyValue的时候再举例子。
看下源码注释:
/*
* By default, the string consisting of prefix+suffix, returned by
* toString(), or properties of value, when no elements have yet been added,
* i.e. when it is empty. This may be overridden by the user to be some
* other value including the empty String.
*/
构造器
从大纲里可以看到StringJoiner总共有两个构造器:

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();
this.emptyValue = this.prefix + this.suffix;
}
这里面三个参数分别是前缀后缀和连接符,然后这里有一个操作就是将emptyValue赋值了前缀+后缀的字符串。也就是说当你用了这个构造器的时候,emptyValue就已经有值了,就是前缀+后缀拼接。当你StringJoiner不执行add方法直接toString()时,会return的对象就是你的前缀+后缀。 例子:
public static void main (String [] args) {
StringJoiner sj = new StringJoiner(":", "[", "]");
String desiredString = sj.toString();
System.out.println(desiredString);
}
这里打印结果就是[]
。
再看只有一个参数的构造器:
public StringJoiner(CharSequence delimiter) {
this(delimiter, "", "");
}
其实还是调用的三参构造器,只不过前后缀默认值为"",也就是没有前后缀。这种情况下emptyValue是什么呢? 其实和上面一样的,""+""就是"",不难理解。
方法
先看上面提到最多的toString().
@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;
}
}
}
当value为空(也就是StringBuilder为空)时,会return emptyVaule, 如果不为空,就会加上一个给你加后缀的操作。当然这里加后缀你可以看作一次性的,他在加之前会取一个长度,不管你后缀多长,加完之后会set到初始长度。
知道了后缀是这里加的,那前缀呢。
来看最核心的 add方法:
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;
}
前缀就是在你调用第一个add的时候就加上了,为什么是第一个呢,因为第一次调用add的时候,你的value肯定是空的,所以它会走else的逻辑给你构造一个StringBuilder出来,这个时候会把前缀加好。所以你后面不管有几个add,都是基于一个StringBuilder上在加字符串。当你的value不为空了,就会给你拼接上连接符,最后再拼接上add()方法里的参数。完美。
再结合toString方法来看,当你没有调用过add方法而直接toString时,value为空,它就会return emptyValue了。
再来看个和emptyValue有关的方法setEmptyValue(CharSequence emptyValue):
public StringJoiner setEmptyValue(CharSequence emptyValue) {
this.emptyValue = Objects.requireNonNull(emptyValue,
"The empty value must not be null").toString();
return this;
}
这个方法其实是暴露给开发者主动设置emptyValue值的方法,也就是说,当你没有调用set方法,emptyValue默认值为前缀+后缀,不管前后缀是否为空;当你调用了set方法,emptyValue就是设置的值。 举个例子:
public static void main (String [] args) {
StringJoiner sj = new StringJoiner(":", "[", "]");
sj.setEmptyValue("anson");
String desiredString = sj.toString();
System.out.println(desiredString);
}
这里的打印结果就是anson
,注意不是[anson]
哦。
还有一个有关字符串操作的方法 merge(StringJoiner)
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;
}
这里面调用了一个StringBuilder的append()重载方法:
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
也就是说merge操作是把另一个StringJoiner(简称sj2)的从前缀开始(不包括前缀)包括连接符但是不包括后缀的字符串加进去sj1里面。
上个例子:
public static void main (String [] args) {
StringJoiner sj1 = new StringJoiner(":","[","]");
StringJoiner sj2 = new StringJoiner(",","{","}");
sj1.add("b");
sj2.add("a").add("c");
sj1.merge(sj2);
String desiredString = sj1.toString();
System.out.println(desiredString);
}
打印结果:[b:a,c]
前后缀都是sj1的,ac间的连接符是sj2的。
最后再看个简单的方法收尾
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());
}
length()方法大家最熟悉,获取长度。这里能看到有一个好处就是当你没有调用add方法也就是没有初始化StringBuilder时,调用这个方法不会空指针,因为有默认的emptyValue。
总结
总结一下,Java 8的新类StringJoiner用法很简单,其实就是一个披着StringJoiner皮的StringBuilder而已嘛。不过有人帮你封装好了加减后缀和连接符的方法也是好的!