文章目录
一.StringJoiner
1.什么是StringJoiner
StringJoiner是Java8
新出的一个类,用于构造由分隔符分隔的字符串
- 可选择性定义每个拼接字符串的前缀以及后缀。
- 可以避免开发人员再次通过StringBuffer或者StingBuilder拼接。
2.StringJoiner源码解析
public final class StringJoiner {
private final String prefix;//前缀
private final String delimiter;//间隔符
private final String suffix;//后缀
private StringBuilder value;//值
private String emptyValue;//空值
public StringJoiner(CharSequence delimiter) {
this(delimiter, "", "");//默认前缀和后缀为"",间隔符为delimiter ,重载调用
}
public StringJoiner(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
//间隔符,前缀和后缀判断是否为null,null将抛出异常
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;//空值被设置为只有前后缀
}
//设置空值,检查是否为null
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();
// reset value to pre-append initialLength
value.setLength(initialLength);
return result;
}
}
}
初始化,先添加前缀,有了之后每次先添加间隔符,StringBuilder后续append字符串
public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement);
return this;
}
//合并StringJoiner,注意后面StringJoiner 的前缀就不要了,后面的appen进来
public StringJoiner merge(StringJoiner other) {
Objects.requireNonNull(other);
if (other.value != null) {
final int length = other.value.length();
// lock the length so that we can seize the data to be appended
// before initiate copying to avoid interference, especially when
// merge 'this'
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() {
// Remember that we never actually append the suffix unless we return
// the full (present) value or some sub-string or length of it, so that
// we can add on more if we need to.
//不忘添加后缀的长度
return (value != null ? value.length() + suffix.length() :
emptyValue.length());
}
}
查看源码可以发现,StringJoiner内部实际上就是使用了StringBuilder,所以拼接效率和StringBuilder几乎是一模一样的,但也有线程不安全的问题
。
3.StringJoiner常用Api
3.1. 构造方法
//构造一个以指定分隔符delimiter分隔每个拼接字符串的StringJoiner
public StringJoiner(CharSequence delimiter)
//构造一个以以指定分隔符delimiter/字符串开头前缀prefix/字符串结尾后缀suffix 拼接字符串的StringJoiner
public StringJoiner(CharSequence delimiter,CharSequence prefix, CharSequence suffix)
使用示例1:以分隔符拼接字符串
@Test
public void TestStringJoiner() {
String[] names = {"Bob", "Alice", "Grace"};
StringJoiner sj = new StringJoiner(", ");
for (String name : names) {
sj.add(name);
}
System.out.println(sj.toString());
}
//执行结果:
//Bob, Alice, Grace
使用示例2:以分隔符+前缀+后缀拼接字符串
@Test
public void TestStringJoiner2() {
String[] names = {"Bob", "Alice", "Grace"};
StringJoiner sj = new StringJoiner(", ", "Hello ", "!");
for (String name : names) {
sj.add(name);
}
System.out.println(sj.toString());
}
//执行结果:
//Hello Bob, Alice, Grace!
3.2. add方法
使用示例:
@Test
public void TestStringJoinerAdd() {
StringJoiner joiner = new StringJoiner("--", "[[[_", "_]]]");
joiner.add("1");
System.out.println("toString: " + joiner.toString());
System.out.println("length: " + joiner.length());
}
分析源码
public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement);
return this;
}
private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter);
} else {
value = new StringBuilder().append(prefix);
}
return value;
}
public AbstractStringBuilder append(CharSequence s) {
if (s == null) return appendNull();
if (s instanceof String) return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
发现StringJoiner底层依旧使用的StringBuilder
,第一次添加数据时,会生成StringBuilder对象,并添加 “前缀”,后续添加字符时,追加 “分隔符”,最后调用 append 方法,最底层调用 System.arraycopy
方法。
public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
src:源数组
srcPos:源数组要复制的起始位置
dest:目的数组
destPos:目的数组放置的起始位置
length:复制的长度
注意:src and dest都必须是同类型或者可以进行转换类型的数组.
3.3. toString
分析源码
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;
}
}
}
- value == null, 返回空。
- value不为空,判断 是否需要添加 后缀
3.4. length
分析源码
public int length() {
// Remember that we never actually append the suffix unless we return
// the full (present) value or some sub-string or length of it, so that
// we can add on more if we need to.
return (value != null ? value.length() + suffix.length() :
emptyValue.length());
}
}
value 不为 null ,返回 value的长度 + 后缀长度(不为null时,已经计算了value+前缀)
3.5. merge
使用示例
@Test
public void TestStringJoinermMerge() {
StringJoiner joiner = new StringJoiner("--", "[[[_", "_]]]");
joiner.add("1").add("2").add("3").add("4");
StringJoiner joiner2 = new StringJoiner("...");
joiner2.add("a").add("b").add("c");
joiner.merge(joiner2);
System.out.println(joiner.toString());
}
分析源码
public StringJoiner merge(StringJoiner other) {
Objects.requireNonNull(other);
if (other.value != null) {
final int length = other.value.length();
// lock the length so that we can seize the data to be appended
// before initiate copying to avoid interference, especially when
// merge 'this'
StringBuilder builder = prepareBuilder();
builder.append(other.value, other.prefix.length(), length);
}
return this;
}
// result
//[[[_1--2--3--4--a...b...c_]]]
3.6. setEmptyValue
用于设置StringJoiner的默认值
分析源码
//设置空值,检查是否为null
public StringJoiner setEmptyValue(CharSequence emptyValue) {
this.emptyValue = Objects.requireNonNull(emptyValue,
"The empty value must not be null").toString();
return this;
}
3.7. merge
用于合并两个joiner
分析源码
public StringJoiner merge(StringJoiner other) {
Objects.requireNonNull(other);
if (other.value != null) {
final int length = other.value.length();
// lock the length so that we can seize the data to be appended
// before initiate copying to avoid interference, especially when
// merge 'this'
StringBuilder builder = prepareBuilder();
builder.append(other.value, other.prefix.length(), length);
}
return this;
}
实例
private String PREFIX = "[";
private String SUFFIX = "]";
@Test
public void whenMergingJoiners_thenReturnMerged() {
StringJoiner rgbJoiner = new StringJoiner(",", PREFIX, SUFFIX);
StringJoiner cmybJoiner = new StringJoiner("-", PREFIX, SUFFIX);
rgbJoiner.add("Red")
.add("Green")
.add("Blue");
cmybJoiner.add("Cyan")
.add("Magenta")
.add("Yellow")
.add("Black");
rgbJoiner.merge(cmybJoiner);
assertEquals( rgbJoiner.toString(), "[Red,Green,Blue,Cyan-Magenta-Yellow-Black]"); //true
}
二.String.join()
在Java8,String还提供了一个静态方法join()
,这个方法在内部使用了StringJoiner来拼接字符串
,在不需要指定“开头”和“结尾”的时候
,用String.join()更方便:
String[] names = {"Bob", "Alice", "Grace"};
String s = String.join(", ", names);
//执行结果: Bob,Alice,Grace
源码分析
String.join()方法源码
//delimiter 分隔符
//elements 需要连接的元素
public static String join(CharSequence delimiter, CharSequence... elements) {
//判断是否为null,如果为null,抛出NullPointerException
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
//构造一个分隔符为delimiter的实例
StringJoiner joiner = new StringJoiner(delimiter);
//循环拼接
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
//delimiter 分隔符
//elements 需要连接字符串集合
public static String join(CharSequence delimiter,
Iterable<? extends CharSequence> elements) {
//判断是否为null,如果为null,抛出NullPointerException
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
//构造一个分隔符为delimiter的实例
StringJoiner joiner = new StringJoiner(delimiter);
//遍历字符串集合elements,循环拼接到joiner中
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
源码分析
StringJoiner构造器源码
//1个参数构造器
public StringJoiner(CharSequence delimiter) {
//调用3个参数构造器
this(delimiter, "", "");
}
//3个参数构造器
//delimiter 分隔符
//prefix 前缀
//suffix 后缀
public StringJoiner(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
//判断是否为null,如果为null,抛出NullPointerException
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;
}
String.join()方法中是通过add方法
拼接字符串的,add()源码如下:
public StringJoiner add(CharSequence newElement) {
//prepareBuilder()返回参数,调用append()方法
prepareBuilder().append(newElement);
return this;
}
perpareBuilder()方法源码如下:
private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter);
} else {
value = new StringBuilder().append(prefix);
}
return value;
}
perpareBuilder()方法返回值是一个StringBuilder
对象,通过调用StringBuilder.append()
方法拼接字符串。
三.利用Stream API实现StringJoiner
@Test
public void whenUsedWithinCollectors_thenJoined() {
List<String> rgbList = Arrays.asList("Red", "Green", "Blue");
String commaSeparatedRGB = rgbList.stream()
.map(color -> color.toString())
.collect(Collectors.joining(","));
assertEquals(commaSeparatedRGB, "Red,Green,Blue");//true
}