String.join()
用分隔符拼接字符串
方法定义
String.join()
是 Java 8 引入的静态方法,用于将多个字符串用指定的分隔符连接成一个字符串。
// 1. 用分隔符连接字符串数组
public static String join(CharSequence delimiter, CharSequence... elements)
// 2. 用分隔符连接 Iterable 中的元素
public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
参数说明:
delimiter
:分隔符,用于连接各个元素的字符序列elements
:要连接的字符串元素(可变参数或 Iterable 集合)
返回值:
- 连接后的字符串
- 如果元素为空或长度为 0,返回空字符串
功能说明
String.join()
提供了一种简洁、高效的方式来将多个字符串用指定分隔符连接。相比传统的字符串拼接方式,它具有以下优势:
- 简洁性:一行代码完成连接操作
- 效率:内部使用
StringBuilder
优化性能 - 可读性:代码意图清晰明确
- 灵活性:支持数组和集合两种输入形式
示例代码
基本用法
public class JoinExample {
public static void main(String[] args) {
// 1. 连接字符串数组
String[] fruits = {"apple", "banana", "orange", "grape"};
String result1 = String.join(", ", fruits);
System.out.println(result1);
// 输出: apple, banana, orange, grape
// 2. 连接 List 集合
List<String> colors = Arrays.asList("red", "green", "blue");
String result2 = String.join(" | ", colors);
System.out.println(result2);
// 输出: red | green | blue
// 3. 使用不同分隔符
String[] words = {"Hello", "world", "Java"};
String result3 = String.join("-", words);
System.out.println(result3);
// 输出: Hello-world-Java
}
}
实际应用场景
// 1. 构建 SQL IN 子句
public static String buildInClause(List<String> values) {
return String.join(",", values.stream()
.map(v -> "'" + v + "'")
.toArray(String[]::new));
}
// 使用示例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String inClause = buildInClause(names);
System.out.println("IN (" + inClause + ")");
// 输出: IN ('Alice','Bob','Charlie')
// 2. 路径拼接
public static String buildPath(String... parts) {
return String.join("/", parts);
}
// 使用示例
String path = buildPath("home", "user", "documents", "file.txt");
System.out.println(path);
// 输出: home/user/documents/file.txt
// 3. CSV 行构建
public static String buildCsvRow(List<String> fields) {
return String.join(",", fields);
}
// 使用示例
List<String> row = Arrays.asList("John", "Doe", "30", "Engineer");
String csvRow = buildCsvRow(row);
System.out.println(csvRow);
// 输出: John,Doe,30,Engineer
特殊情况处理
// 1. 空数组或集合
String[] emptyArray = {};
List<String> emptyList = new ArrayList<>();
System.out.println(String.join(",", emptyArray)); // 输出空字符串
System.out.println(String.join(",", emptyList)); // 输出空字符串
// 2. 包含空字符串的元素
String[] withEmpty = {"a", "", "c", "d"};
System.out.println(String.join("-", withEmpty));
// 输出: a--c-d
// 3. 单个元素
String[] single = {"only"};
System.out.println(String.join(",", single));
// 输出: only (没有分隔符)
// 4. null 元素(会抛出 NullPointerException)
// String[] withNull = {"a", null, "c"};
// String.join(",", withNull); // 会抛出异常
使用技巧
1. 与 Stream 配合使用
// 处理数据并连接
List<String> names = Arrays.asList("alice", "bob", "charlie");
String result = names.stream()
.map(String::toUpperCase)
.collect(Collectors.joining(", "));
System.out.println(result);
// 输出: ALICE, BOB, CHARLIE
// 使用 Collectors.joining() 的更多选项
String detailed = names.stream()
.map(String::toUpperCase)
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(detailed);
// 输出: [ALICE, BOB, CHARLIE]
2. 处理不同数据类型
// 连接数字
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
String numberStr = numbers.stream()
.map(String::valueOf)
.collect(Collectors.joining("-"));
System.out.println(numberStr);
// 输出: 1-2-3-4-5
// 连接对象
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30)
);
String peopleStr = people.stream()
.map(Person::getName)
.collect(Collectors.joining(", "));
System.out.println(peopleStr);
// 输出: Alice, Bob
3. 构建复杂字符串
// 构建查询参数
Map<String, String> params = new HashMap<>();
params.put("name", "john");
params.put("age", "30");
params.put("city", "ny");
String queryString = params.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
System.out.println(queryString);
// 输出: name=john&age=30&city=ny
常见错误
1. 忽略 null 值
// 错误:直接包含 null 值
// String[] withNull = {"a", null, "c"};
// String.join(",", withNull); // NullPointerException
// 正确:过滤 null 值
String[] withNull = {"a", null, "c"};
String result = Arrays.stream(withNull)
.filter(Objects::nonNull)
.collect(Collectors.joining(","));
System.out.println(result); // a,c
2. 分隔符选择错误
// 错误:使用了不需要的分隔符
String[] parts = {"file", "txt"};
String wrong = String.join("", parts); // filetxt (缺少点号)
// 正确:使用正确的分隔符
String correct = String.join(".", parts); // file.txt
3. 性能误区
// 不推荐:在循环中多次调用 join
List<String> results = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
String[] data = getData(i);
results.add(String.join(",", data)); // 每次创建新字符串
}
// 推荐:批量处理或使用 StringBuilder
注意事项
- null 安全性:
String.join()
本身不处理 null 元素,会抛出NullPointerException
- 性能考虑:对于大量数据的连接,考虑使用
StringBuilder
或StringBuffer
- 内存使用:大字符串连接会产生大量临时对象
- 编码问题:确保所有字符串使用相同的字符编码
- 线程安全:
String.join()
是线程安全的,但结果字符串的使用需要注意
最佳实践
1. 创建工具方法
public class StringUtils {
// 安全的 join 方法,过滤 null 值
public static String safeJoin(CharSequence delimiter, String... elements) {
if (elements == null || elements.length == 0) {
return "";
}
return Arrays.stream(elements)
.filter(Objects::nonNull)
.collect(Collectors.joining(delimiter));
}
// 连接路径部分
public static String joinPath(String... parts) {
return Arrays.stream(parts)
.filter(Objects::nonNull)
.filter(s -> !s.isEmpty())
.collect(Collectors.joining("/"));
}
}
2. 处理复杂数据结构
// 连接嵌套集合
public static String joinNested(List<List<String>> nested) {
return nested.stream()
.map(list -> String.join("-", list))
.collect(Collectors.joining("|"));
}
// 使用示例
List<List<String>> data = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
System.out.println(joinNested(data)); // a-b|c-d
3. 性能优化
// 对于大量数据的连接
public static String joinLargeDataset(List<String> data, String delimiter) {
// 预估 StringBuilder 容量
int estimatedLength = data.stream()
.mapToInt(String::length)
.sum() + (data.size() - 1) * delimiter.length();
StringBuilder sb = new StringBuilder(Math.max(16, estimatedLength));
for (int i = 0; i < data.size(); i++) {
if (i > 0) {
sb.append(delimiter);
}
sb.append(data.get(i));
}
return sb.toString();
}
4. 验证输入
public static String validatedJoin(String delimiter, String... elements) {
if (delimiter == null) {
throw new IllegalArgumentException("Delimiter cannot be null");
}
if (elements == null) {
return "";
}
return Arrays.stream(elements)
.filter(Objects::nonNull)
.collect(Collectors.joining(delimiter));
}
性能优化
1. 预估容量
// 对于已知大小的数据集
public static String joinWithCapacity(List<String> items, String delimiter) {
if (items.isEmpty()) return "";
// 预估最终字符串长度
int totalLength = items.stream()
.mapToInt(String::length)
.sum() + (items.size() - 1) * delimiter.length();
StringBuilder sb = new StringBuilder(Math.max(16, totalLength));
for (int i = 0; i < items.size(); i++) {
if (i > 0) sb.append(delimiter);
sb.append(items.get(i));
}
return sb.toString();
}
2. 批量处理
// 对于多个连接操作
public static List<String> batchJoin(List<List<String>> datasets, String delimiter) {
return datasets.parallelStream() // 使用并行流
.map(list -> String.join(delimiter, list))
.collect(Collectors.toList());
}
3. 缓存常用结果
// 对于频繁使用的连接结果
private static final Map<List<String>, String> JOIN_CACHE = new ConcurrentHashMap<>();
public static String cachedJoin(String delimiter, List<String> elements) {
return JOIN_CACHE.computeIfAbsent(elements,
list -> String.join(delimiter, list));
}
总结
String.join()
是 Java 中简洁高效的字符串连接工具,关键要点:
- 核心功能:用指定分隔符连接多个字符串
- 简洁性:相比传统拼接方式更简洁易读
- 灵活性:支持数组和集合两种输入形式
- 性能:内部使用 StringBuilder 优化
- 最佳实践:
- 处理 null 值和边界情况
- 对于复杂需求结合 Stream 使用
- 考虑性能优化和内存使用
- 创建工具方法提高代码复用性
掌握 String.join()
方法能有效简化字符串连接操作,特别是在构建 SQL、路径、CSV 等格式化字符串时非常有用。