Java 8中StringJoiner的字符串拼接优势及在IDEA中的使用技巧
在《为什么阿里巴巴不建议在 for 循环中使用“+”进行字符串拼接》这篇文章里,我们阐述了 Java 中几种字符串拼接的方式以及它们的优缺点。其中还有一种重要的拼接方式未被介绍,那便是 Java 8 中所提供的 StringJoiner ,接下来本文就来对这个字符串拼接的新成员进行介绍。

如果你想知晓有多少种方式能够进行字符串的拼接,我来教你一个简便的办法。在 Intellij IDEA 里,先定义一个 Java Bean,接着尝试利用快捷键自动生成一个 toString 方法,此时 IDEA 会给出多种 toString 生成策略供你选择。

目前我所使用的 IDEA,其 toString 生成策略默认是采用 JDK 1.8 所提供的 StringJoiner。
介绍
StringJoiner 属于 java.util 包。它可用于构建由分隔符分隔的字符序列(此为可选操作)。还能从给定的前缀开始,并以给定的后缀结束。虽然借助 StringBuilder 类可在每个字符串之后附加分隔符,但 StringJoiner 提供了简便的实现方式,无需编写大量代码。
StringJoiner类有 2 个构造函数,还有 5 个公有方法。在这些方法当中,最常用的方法分别是 add 方法以及 toString 方法,这与 StringBuilder 中的 append 方法和 toString 方法类似。
用法
StringJoiner 的用法较为简单。在下面的代码里,我们借助 StringJoiner 进行了字符串的拼接。
类 StringJoinerTest 属于公共类。
公共静态 void 主方法(String[] 参数)。
创建了一个名为 sj 的 StringJoiner 对象,使用 "Hollis" 作为分隔符。
sj.add("hollischuang");
sj.add("Java干货");
输出 sj 的字符串表示形式。具体来说,就是调用 sj 对象的 toString() 方法,并将其返回值通过 System.out.println() 方法打印到控制台。
创建了一个名为 sj1 的 StringJoiner 对象,使用冒号 : 作为分隔符,方括号 [ 和 ] 分别作为前缀和后缀。
sj1 进行了添加操作,添加了 "Hollis";sj1 还进行了添加操作,添加了 "hollischuang";sj1 又进行了添加操作,添加了 "Java 干货"。
System 把 sj1 转换为字符串并进行输出。
}
}
以上代码输出结果:
hollischuangHollisJava干货
[Hollis:hollischuang:Java干货]
值得注意的是,当我们用 StringJoiner(CharSequence delimiter)来初始化一个 StringJoiner 时,这个 delimiter 实际上是分隔符,而不是可变字符串的初始值。
StringJoiner 的第二个参数是拼接后的字符串的前缀,第三个参数是拼接后的字符串的后缀。
原理
在介绍了简单的用法之后,我们接着来看这个 StringJoiner 的原理,去看看它究竟是怎样实现的。重点来看一下 add 方法。
public StringJoiner 向其中添加一个字符序列类型的新元素。
准备构建器。将新元素追加到构建器中。
return this;
}
构建一个 StringBuilder 用于后续操作。
if (value != null) {
value.append(delimiter);
} else {
使用 StringBuilder 创建一个新的对象,然后通过 append 方法将 prefix 追加到该对象中,最后将结果赋值给 value 。
}
return value;
}
看到了一个熟悉的身影,那就是 StringBuilder 。确实,StringJoiner 实际上是依靠 StringBuilder 来实现的。在之前关于“为什么阿里巴巴不建议在 for 循环中使用‘+’进行字符串拼接”的内容里,我们已经介绍过 StringBuilder 的实现原理,而本文不再对其进行重复阐述。
我们发现 StringJoiner 是通过 StringBuilder 实现的。之后,我们大概就能猜到,它的性能损耗应该和直接使用 StringBuilder 差不多。
为什么需要StringJoiner
了解了 StringJoiner 的用法和原理之后,很多读者可能会产生一个疑问。那就是明明已经有了一个 StringBuilder,可 Java 8 中为什么还要定义一个 StringJoiner 呢?它到底有什么好处呢?
如果读者对 Java 8 有足够的了解,或许能够猜出大概情况。这肯定与 Stream 存在关联。
作者也在Java doc中找到了答案:
可以使用 StringJoiner 从 Stream 中创建格式化输出,通过 Collectors.joining(CharSequence) 来实现。
试想,在Java中,如果我们有这样一个List:
List- 这个列表中包含了 "Java 干货"。
如果我们想要把他拼接成一个以下形式的字符串:
Hollis,hollischuang,Java干货
可以通过以下方式:
创建了一个 StringBuilder 对象。
if (!list.isEmpty()) {
builder.append(list.get(0));
初始化变量 i 为 1,同时获取列表 list 的大小并赋值给变量 n。当 i 小于 n 时,执行循环体中的代码。每次循环,i 的值增加 1。在循环体中,可以对列表中的元素进行各种操作,例如访问、修改或删除等。循环结束后,继续执行后续的代码。< n; i++) {
builder.append(",").append(list.get(i));
}
}
builder.toString();
还可以使用:
先创建一个空的 StringBuilder 对象,然后通过 stream 中的每个元素 s 与之前的 StringBuilder 对象 sb 进行拼接,拼接后再继续与下一个元素进行拼接,直到 stream 中的所有元素都拼接完毕,最后使用 StringBuilder::append 方法进行合并,最后将合并后的结果转换为字符串。
但是输出结果稍有些不同,需要进行二次处理:
Hollis,hollischuang,Java干货,
还可以使用"+"进行拼接:
通过 list 的 stream 进行操作,先使用 reduce 方法,将元素 a 和 b 进行连接,连接方式为 a 加上逗号再加 b,最后将结果转换为字符串并返回。
以上几种方式,有的代码较为复杂;有的性能不够高;还有的无法直接获取到想要的结果。
为了满足类似的需求,在 Java 8 中,StringJoiner 发挥了作用。上述需求仅需一行代码:
使用 stream 对 list 进行操作,然后通过 Collectors 的 joining 方法,以冒号(:)作为连接符进行收集。
上面所使用的表达式里,Collectors.joining 的源代码情况如下:
public static Collector通过分隔符来实现连接的操作。
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl<>(
使用 delimiter 作为分隔符,prefix 作为前缀,suffix 作为后缀来创建一个新的 StringJoiner 对象,即 new StringJoiner(delimiter, prefix, suffix)。
StringJoiner 可以使用 add 方法进行添加操作,也可以使用 merge 方法进行合并操作。
使用 StringJoiner 的 toString 方法,传入 CH_NOID 作为参数。
}
其实现原理就是借助了StringJoiner。
或许在 Collector 中直接使用 StringBuilder 能够实现类似的功能,只是会稍微麻烦一些。因此,Java 8 提供了 StringJoiner 以丰富 Stream 的用法。
StringJoiner能够方便地添加前缀和后缀。例如,若我们期望得到的字符串是[Hollis,hollischuang,Java 干货],而非仅仅是 Hollis,hollischuang,Java 干货,那么 StringJoiner 的优势就会更加显著。
总结
本文介绍了 Java 8 中所提供的一种可变字符串类,即 StringJoiner。这种类可以被用来进行字符串的拼接操作。
StringJoiner是通过StringBuilder来实现的。因此,它的性能与StringBuilder相近。并且,它也是非线程安全的。
如果日常开发中中,需要进行字符串拼接,如何选择?
1、如果只是简单的字符串拼接,考虑直接使用"+"即可。
如果是在 for 循环里进行字符串拼接的话,那就考虑使用 StringBuilder 或者 StringBuffer 。
如果是用一个 List 来进行字符串的拼接,那么就可以考虑使用 StringJoiner 。