目录
- 前言
- 1. 差异对比
- 2. 基本知识
- 3. 源码分析
- 4. Demo
前言
对于Java的基本知识推荐阅读:
- java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
- 【Java项目】实战CRUD的功能整理(持续更新)
1. 差异对比
展示如何使用StringBuilder和StringJoiner来构建一个以逗号分隔的字符串,并比较这两者的实现方式
import java.util.StringJoiner;
public class StringJoinerExample {
public static void main(String[] args) {
// 创建 StringJoiner,使用逗号作为分隔符
StringJoiner joiner = new StringJoiner(", ");
// 添加元素
joiner.add("苹果");
joiner.add("香蕉");
joiner.add("橙子");
// 打印最终结果
System.out.println("StringJoiner 结果: " + joiner.toString());
}
}
public class StringBuilderExample {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
// 要添加的元素
String[] fruits = {"苹果", "香蕉", "橙子"};
for (int i = 0; i < fruits.length; i++) {
builder.append(fruits[i]); // 添加元素
if (i < fruits.length - 1) {
builder.append(", "); // 添加分隔符
}
}
// 打印最终结果
System.out.println("StringBuilder 结果: " + builder.toString());
}
}
两者都是输出:苹果, 香蕉, 橙子
两者的差异比较如下:
特性 | StringJoiner | StringBuilder |
---|---|---|
用法简洁性 | 提供直接的add方法,自动处理分隔符 | 需要手动管理分隔符的添加 |
空值处理 | 支持自定义空值表示 | 不支持直接的空值表示 |
合并操作 | 轻松合并其他StringJoiner | 不支持合并其他实例 |
代码可读性 | 更清晰,意图明显 | 代码较为复杂,需要更多逻辑控制 |
适用场景 | 适合需要生成带分隔符的字符串时 | 适合一般的字符串拼接需求 |
2. 基本知识
StringJoiner 是一个用于构建由分隔符连接的字符串序列的工具,在Java 8中引入,旨在简化字符串连接的过程
主要构造方法
StringJoiner(CharSequence delimiter)
:使用给定的分隔符创建一个StringJoiner实例StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
:使用给定的分隔符、前缀和后缀创建一个StringJoiner实例
主要方法
add(CharSequence newElement)
:向StringJoiner中添加一个新元素merge(StringJoiner other)
:将另一个StringJoiner中的元素合并到当前实例toString()
:返回构建的字符串
3. 源码分析
整体逻辑总结
-
构造与初始化:StringJoiner 提供两个构造方法,可以仅指定分隔符,也可以同时指定前缀和后缀。在构造时,会确保前缀、分隔符和后缀不为null
-
添加元素:使用 add 方法可以向StringJoiner中添加新元素,内部调用prepareBuilder方法准备一个StringBuilder来构建字符串
-
字符串合并:merge 方法允许将另一个StringJoiner的元素合并到当前实例中
-
返回拼接结果:toString 方法会返回当前的拼接结果。如果没有添加任何元素,则返回空值表示
-
长度管理:length 方法返回当前拼接的字符串长度,考虑了后缀的长度
完整的源码分析如下:
public class StringJoiner {
private final String delimiter; // 分隔符
private final String prefix; // 前缀
private final String suffix; // 后缀
private StringBuilder value; // 存储拼接结果
private String emptyValue; // 空值表示
// 构造方法,接受分隔符
public StringJoiner(CharSequence delimiter) {
this(delimiter, "", ""); // 默认前缀和后缀为空
}
// 构造方法,接受分隔符、前缀和后缀
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");
// 创建参数的防御性拷贝
this.prefix = prefix.toString();
this.delimiter = delimiter.toString();
this.suffix = suffix.toString();
this.emptyValue = this.prefix + this.suffix; // 设置空值表示
}
// 设置空值表示
public StringJoiner setEmptyValue(CharSequence emptyValue) {
this.emptyValue = Objects.requireNonNull(emptyValue, "The empty value must not be null").toString();
return this;
}
// 返回拼接后的字符串
@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(); // 拼接后缀
// 重置值为初始长度,准备下一次添加
value.setLength(initialLength);
return result;
}
}
}
// 添加新元素
public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement); // 准备构建器并添加新元素
return this;
}
// 合并另一个StringJoiner
public StringJoiner merge(StringJoiner other) {
Objects.requireNonNull(other); // 确保其他StringJoiner不为null
if (other.value != null) {
final int length = other.value.length();
// 锁定长度以避免干扰
StringBuilder builder = prepareBuilder();
builder.append(other.value, other.prefix.length(), length); // 复制元素
}
return this;
}
// 准备构建器
private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter); // 如果已经有值,添加分隔符
} else {
value = new StringBuilder().append(prefix); // 初始化构建器并添加前缀
}
return value;
}
// 返回当前长度
public int length() {
// 返回值的长度加上后缀的长度,或者空值的长度
return (value != null ? value.length() + suffix.length() : emptyValue.length());
}
}
具体设计逻辑分析如下:
-
StringBuilder的使用:StringJoiner内部使用StringBuilder来动态拼接字符串。在调用add方法时,它会立即添加前缀和分隔符,使得每次添加元素时,构建的字符串是连贯的。这样设计的好处在于,后续的合并操作非常简便,因为拼接的逻辑已经被简化,用户只需关注添加元素
-
后缀的延迟添加:后缀只有在调用toString或length方法时才会被加上。这种设计方式确保了拼接字符串的格式保持灵活,用户可以根据需要动态生成最终字符串。例如,当有条件地添加后缀时,这种延迟策略避免了不必要的计算和内存占用
-
合并操作的便利性:由于StringBuilder在拼接过程中已经考虑了分隔符和前缀,merge操作可以非常简单。它只需将另一个StringJoiner的内容直接附加到当前实例,无需再关心前缀和分隔符的处理,从而减少了复杂性
emptyValue的构造
-
构造时生成:在构造StringJoiner时,会生成emptyValue,即由前缀和后缀组成的字符串。这一设计是为了确保在没有元素添加时,能快速返回一个默认值,避免后续计算时出现空值
-
用户自定义默认值:用户如果希望有自己的默认值,可以使用setEmptyValue方法。虽然这样需要先构造实例再进行注入,但这种灵活性是设计的一部分,允许用户根据具体需求定制默认值
4. Demo
import java.util.StringJoiner;
public class StringJoinerDemo {
public static void main(String[] args) {
// 创建StringJoiner,使用逗号作为分隔符
StringJoiner joiner = new StringJoiner(", ", "[", "]");
// 设置自定义的空值表示
joiner.setEmptyValue("没有元素");
// 打印当前的结果(没有添加元素)
System.out.println("当前结果: " + joiner.toString());
// 添加元素
joiner.add("苹果");
joiner.add("香蕉");
// 打印添加元素后的结果
System.out.println("添加元素后的结果: " + joiner.toString());
// 合并另一个StringJoiner
StringJoiner anotherJoiner = new StringJoiner(", ", "{", "}");
anotherJoiner.add("西瓜");
anotherJoiner.add("葡萄");
// 合并
joiner.merge(anotherJoiner);
// 打印合并后的结果
System.out.println("合并后的结果: " + joiner.toString());
}
}
截图如下: