优先使用 Collectors.joining() 进行字符串拼接,它底层基于 StringBuilder、线程安全且支持分隔符与前后缀;复杂格式才用 reduce 配合 StringBuilder,避免在 forEach 中拼接。

Java 中用 StringBuilder 配合流(Stream)做字符串拼接,关键在于避免在流的中间操作里反复创建 StringBuilder 实例,而应把流收集为集合后统一构建,或使用 Collectors.joining() 这类专为拼接设计的收集器——它底层就用到了 StringBuilder,且线程安全、高效。
优先用 Collectors.joining() 替代手动 StringBuilder
这是最简洁、最推荐的方式。它专为字符串拼接优化,内部使用 StringBuilder,支持分隔符、前缀、后缀,且无需手动管理对象生命周期。
- 基础拼接:
list.stream().collect(Collectors.joining()) - 带分隔符:
list.stream().collect(Collectors.joining(", ")) - 带前后缀:
list.stream().collect(Collectors.joining(", ", "[", "]"))
例如:Arrays.asList("a", "b", "c").stream().collect(Collectors.joining("-")) 返回 "a-b-c",性能好、代码干净。
需要复杂格式时再手动用 StringBuilder + reduce
如果拼接逻辑不能靠简单分隔符表达(比如每个元素要加编号、条件包裹、嵌套格式),可借助 Stream.reduce(),传入一个初始 StringBuilder 和累加器函数。
立即学习“Java免费学习笔记(深入)”;
- 注意:必须用
new StringBuilder()作为 identity,否则并发流会出错 - 累加器函数要返回同一个
StringBuilder实例(即调用append()后 return this) - 不建议在
map或forEach中新建StringBuilder,那会失去复用优势
示例:list.stream().reduce(new StringBuilder(), (sb, s) -> sb.append("Item: ").append(s).append("; "), (a, b) -> a.append(b))
避免常见陷阱:别在 forEach 里拼接
stream.forEach() 是无序、不可组合的操作,不适合累积拼接。它不保证执行顺序,也不返回结果,强行在里面用 StringBuilder 会破坏流式语义,还可能引发并发问题。
- ❌ 错误写法:
StringBuilder sb = new StringBuilder(); list.stream().forEach(s -> sb.append(s)); - ✅ 正确替代:
list.stream().collect(Collectors.joining())或reduce方式
这种写法既违背函数式原则,又难以测试和复用,纯属退化为循环。
大数据量时考虑预估容量
如果已知拼接后字符串大致长度(比如每个元素平均 10 字符,共 1000 个),可在初始化 StringBuilder 时指定容量,减少数组扩容开销。
- 例如:
new StringBuilder(expectedTotalLength) - 搭配
reduce使用时,identity 参数可传入预设容量的实例 - 但
Collectors.joining()已做了类似优化,一般无需手动干预
预估过头影响不大,预估不足才真正拖慢性能;日常开发中优先信赖标准 API 的实现。


















