【Java基础】Java8新特性-StringJoiner

一.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都必须是同类型或者可以进行转换类型的数组.

扫描二维码关注公众号,回复: 11382418 查看本文章

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
}

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/104370129