字符串拼接新姿式—— Java 8 StringJoiner

2,100 阅读5分钟

前言

日常工作中拼接字符串肯定是我们少不了的操作,最近工作中正好用到了,然后想起前几天看到的Java 8的一个新类:StringJoiner类,所以今天正好来看一看它的源码。

正文

用法简介

这个类的用法其实很简单,我们可以看到源码中给出的例子:

 StringJoiner sj = new StringJoiner(":", "[", "]");
 sj.add("George").add("Sally").add("Fred");
 String desiredString = sj.toString();

我们把这个字符串最终打印出来:

[George:Sally:Fred]

所以基本上能看出一点用法了: 通过带参构造器构造出一个StringJoiner的对象,调用add方法开始拼接字符串,构造器中的参数会决定拼接的字符串的前后缀以及中间的连接符,最终调用toString方法转换为String对象。

一张图总览下这个类里都有些什么,后面逐一解释。

参数

StringJoiner总共有五个参数:

private final String prefix;    //前缀
private final String delimiter; //分隔符
private final String suffix;    //后缀
private StringBuilder value;  
private String emptyValue;   

后两个参数很有意思

private StringBuilder value; 
其实是Joiner的底层,说到底StringJoiner还是调用的StringBuiler方法,只是这层封装里加上了有关于前缀,后缀和连接符的操作,让我们可以方便一些。
 private String emptyValue;
 emptyValue 你可以把它看作是当你的StringJoiner对象没有进行任何add的操作时,调用toString() 方法会return 这个字符串而不是空。具体的用法后面看到setEmptyValue的时候再举例子。

看下源码注释:

 /*
     * By default, the string consisting of prefix+suffix, returned by
     * toString(), or properties of value, when no elements have yet been added,
     * i.e. when it is empty.  This may be overridden by the user to be some
     * other value including the empty String.
     */
构造器

从大纲里可以看到StringJoiner总共有两个构造器:

先看参数多的

 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");
        // make defensive copies of arguments
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix;
    }

这里面三个参数分别是前缀后缀和连接符,然后这里有一个操作就是将emptyValue赋值了前缀+后缀的字符串。也就是说当你用了这个构造器的时候,emptyValue就已经有值了,就是前缀+后缀拼接。当你StringJoiner不执行add方法直接toString()时,会return的对象就是你的前缀+后缀。 例子:

public static void main (String [] args) {

		StringJoiner sj = new StringJoiner(":", "[", "]");
		String desiredString = sj.toString();
		System.out.println(desiredString);
	}

这里打印结果就是[]

再看只有一个参数的构造器:

 public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }

其实还是调用的三参构造器,只不过前后缀默认值为"",也就是没有前后缀。这种情况下emptyValue是什么呢? 其实和上面一样的,""+""就是"",不难理解。

方法

先看上面提到最多的toString().

 @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;
            }
        }
    }

当value为空(也就是StringBuilder为空)时,会return emptyVaule, 如果不为空,就会加上一个给你加后缀的操作。当然这里加后缀你可以看作一次性的,他在加之前会取一个长度,不管你后缀多长,加完之后会set到初始长度。

知道了后缀是这里加的,那前缀呢。

来看最核心的 add方法:

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;
    }

前缀就是在你调用第一个add的时候就加上了,为什么是第一个呢,因为第一次调用add的时候,你的value肯定是空的,所以它会走else的逻辑给你构造一个StringBuilder出来,这个时候会把前缀加好。所以你后面不管有几个add,都是基于一个StringBuilder上在加字符串。当你的value不为空了,就会给你拼接上连接符,最后再拼接上add()方法里的参数。完美。

再结合toString方法来看,当你没有调用过add方法而直接toString时,value为空,它就会return emptyValue了。

再来看个和emptyValue有关的方法setEmptyValue(CharSequence emptyValue):

 public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }

这个方法其实是暴露给开发者主动设置emptyValue值的方法,也就是说,当你没有调用set方法,emptyValue默认值为前缀+后缀,不管前后缀是否为空;当你调用了set方法,emptyValue就是设置的值。 举个例子:

public static void main (String [] args) {

		StringJoiner sj = new StringJoiner(":", "[", "]");
		sj.setEmptyValue("anson");
		String desiredString = sj.toString();
		System.out.println(desiredString);
	}

这里的打印结果就是anson,注意不是[anson]哦。

还有一个有关字符串操作的方法 merge(StringJoiner)

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;
    }

这里面调用了一个StringBuilder的append()重载方法:

 public StringBuilder append(CharSequence s, int start, int end) {
        super.append(s, start, end);
        return this;
    }

也就是说merge操作是把另一个StringJoiner(简称sj2)的从前缀开始(不包括前缀)包括连接符但是不包括后缀的字符串加进去sj1里面。

上个例子:

public static void main (String [] args) {

		StringJoiner sj1 = new StringJoiner(":","[","]");
		StringJoiner sj2 = new StringJoiner(",","{","}");
		sj1.add("b");
		sj2.add("a").add("c");
		sj1.merge(sj2);
		String desiredString = sj1.toString();
		System.out.println(desiredString);
	}

打印结果:[b:a,c]

前后缀都是sj1的,ac间的连接符是sj2的。

最后再看个简单的方法收尾

 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());
    }

length()方法大家最熟悉,获取长度。这里能看到有一个好处就是当你没有调用add方法也就是没有初始化StringBuilder时,调用这个方法不会空指针,因为有默认的emptyValue。

总结

总结一下,Java 8的新类StringJoiner用法很简单,其实就是一个披着StringJoiner皮的StringBuilder而已嘛。不过有人帮你封装好了加减后缀和连接符的方法也是好的!