StringJoiner
import java.util.StringJoiner;
StringJoiner 是 Java 8 引入的工具类,用于构造由分隔符连接的字符串序列,并支持添加前缀和后缀。它能自动处理分隔符插入,避免手动拼接字符串时的边界判断问题(例如最后一个元素后不加分隔符)。
核心功能
方法/构造器 | 作用说明 |
---|---|
new StringJoiner(" ") | 创建一个分隔符为空格、无前缀/后缀的拼接器 |
new StringJoiner(",", "[", "]") | 创建分隔符为逗号、前缀为 [ 、后缀为 ] 的拼接器(例如 [a,b,c] ) |
add(String str) | 添加一个字符串元素,自动插入分隔符 |
merge(StringJoiner other) | 合并另一个 StringJoiner 的内容(保留当前分隔符和前后缀) |
toString() | 生成最终字符串 |
length() | 返回长度**(是字符出现的个数,包括前缀后缀和间隔符,并不是add方法添加的个数)** |
代码示例
StringJoiner sj = new StringJoiner("-", "【", "】");
sj.add("Java");
sj.add("Python");
sj.add("C++");
System.out.println(sj.toString()); // 输出:【Java-Python-C++】
System.out.println(sj.length()); // 输出:17 而不是3
与传统方法对比
使用 StringBuilder(需手动处理分隔符)
StringBuilder sb = new StringBuilder("【");
String[] arr = {"Java", "Python", "C++"};
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append("-"); // 判断是否为最后一个元素
}
}
sb.append("】");
System.out.println(sb.toString());
使用 StringJoiner(自动处理分隔符)
StringJoiner sj = new StringJoiner("-", "【", "】");
for (String s : arr) {
sj.add(s);
}
System.out.println(sj.toString());
关键优势
-
自动分隔符处理:无需手动判断是否为最后一个元素,代码更简洁且不易出错。
-
支持前缀/后缀:可一键添加统一的前后缀(如生成 JSON 数组、带括号列表)。
-
链式调用支持:例如:
String result = new StringJoiner(",") .add("A").add("B").add("C") .toString(); // "A,B,C"
-
与 Stream API 集成:可用于
Collectors.joining()
,简化流操作。
适用场景: 日志拼接、生成 CSV 数据、构建格式化字符串(如 JSON 数组、SQL 的 IN 条件)以及频繁动态拼接字符串。
字符串拼接的底层原理
纯字符串常量
当纯字符串常量拼接时(无变量参与),触发编译期优化机制,直接合并为完整字符串常量
public class Test {
public static void main(String[] args) {
String s = "a" + "b" + "c"; // 连续字符串拼接
System.out.println(s);
}
}
在编译为class文件后:
public class Test {
public static void main(String[] args) {
String s = "abc"; // 优化后的等效代码,可以直接复用串池中的"abc"(如果存在的话)
System.out.println(s);
}
}
常量加变量
Java8以前
底层调用的是StringBuilder
的append()
方法来拼接字符串,并通过toString()
生成一个String
对象(底层是new出来一个String对象):
// 编译器转换后的等效代码(图像右侧示例)
String s2 = new StringBuilder().append(s1).append("b").toString();
一个加号在堆中会创建两个对象
Java8
有很多种方式,这个只是默认
先预估要拼接的长度并创建一个数组:
通过数组来创建字符串,减少创建的对象:
String s1 = "Hello";
String s2 = "World";
String result = s1 + s2 + "c";
在 JDK 7 及更早版本中
s1 + s2
会创建一个临时的StringBuilder
,并将其转换为一个新的String
对象。- 然后再用这个新对象与
"c"
拼接,再次创建一个新的String
对象。 - 总共可能涉及多次扩容和对象创建。
在 JDK 7 Update 6+(含 Java 8)中
- JVM 会预估最终字符串的长度为
s1.length() + s2.length() + "!".length()
。 - 创建一个初始容量合适的
StringBuilder
,并将所有部分一次性拼接。 - 最终只生成一个
String
对象。
在Java 8中,对于字符串拼接的底层优化主要体现在
String
类的+
操作符上。在JDK 5及以后版本中,编译器会对字符串字面量(常量)的拼接进行静态优化,直接在编译期间合并成一个字符串常量存储到常量池中,这不会涉及运行时创建额外对象。而对于非字符串字面量的动态拼接,即在运行时才知道拼接内容的情况,在Java 7及更早版本中,每使用一次
+
运算符拼接字符串,都会生成一个新的String
对象,这是因为String
是不可变的,每次拼接都需要创建新的char[]
数组来存储结果。然而,从Java 7 Update 6之后的一个性能改进开始(这项改进后来也包含在所有后续版本中,包括Java 8),JVM实现了一种称为“字符串串联缓存”(String Concatenation Cache)的技术。对于通过
StringBuilder
或StringBuffer
完成字符串连接的地方,如果代码看起来像是连续的String
变量和字面量的拼接,JVM会自动优化为使用StringBuilder
来执行这些操作,并且它会尝试预估最终字符串的长度以减少数组扩容带来的性能损耗。具体来说,当JVM检测到可能的字符串拼接循环或多次拼接时,它会创建一个内部的字符数组(
char[]
),并根据需要逐步添加字符串的内容,而不是每次都创建新的String
对象。这个内部的字符数组就是用来构建最终拼接结果的缓冲区。因此,尽管没有明确的方法可以直接查看JVM是否以及如何创建了内部的字符数组,但可以确认的是,JVM在处理非编译期优化的字符串拼接时,确实采用了更为高效的方式,利用类似于
StringBuilder
的机制,并预估和分配足够的字符数组空间来避免频繁的内存重分配和复制操作。
Java9及以后(待补充)
集合
ArrayList 成员方法
操作 | 方法名 | 说明 |
---|---|---|
增 | boolean add(E e) | 添加元素,返回值表示是否添加成功 |
删 | boolean remove(E e) | 删除指定元素,返回值表示是否删除成功 |
删 | E remove(int index) | 删除指定索引位置的元素 |
改 | E set(int index, E e) | 修改指定索引位置的元素,返回修改前的元素 |
查 | E get(int index) | 获取指定索引位置的元素 |
查 | int size() | 返回集合长度,即集合中元素的个数 |