StringJoiner拼接字符串(使用以及源码解析)

15 篇文章 0 订阅

文章目录

1. 介绍

  1. StringJoiner类是jdk8推出的,用于构造由分隔符分隔字符序列,并可选择性地从提供的前缀开始和以提供的后缀结尾
  2. 说的通俗一点:就是让字符串之间有分隔符或者字符串有前缀或者后缀,不用开发人员自己拼接。

2. 代码演示

public class demo {
    public static void main(String[] args) {
    	// 第一个参数:分隔符
        StringJoiner stringJoiner = new StringJoiner(",");
        stringJoiner.add("hello")
                    .add("world");
                    
        System.out.println(stringJoiner.toString());
        System.out.println(stringJoiner.length());
    }
}

在这里插入图片描述

public class demo {
    public static void main(String[] args) {
    	// 第一个参数:分隔符
    	// 第二个参数:前缀符
    	// 第三个参数:后缀符
        StringJoiner stringJoiner = new StringJoiner(",","kk","dd");
        stringJoiner.add("hello")
                    .add("world");

        System.out.println(stringJoiner.toString());
        System.out.println(stringJoiner.length());
    }
}

在这里插入图片描述
细心的人发现:加了前缀kk,后缀dd,字符串长度从刚刚的11变成了15,即StringJoiner会自动帮你加上前缀后缀的长度

public class demo {
    public static void main(String[] args) {
    	// 不add字符串,看看会返回什么
        StringJoiner stringJoiner = new StringJoiner(",","kk","dd");

        System.out.println(stringJoiner.toString());
        System.out.println(stringJoiner.length());
    }
}

在这里插入图片描述
返回的是前后缀字符串拼接,因为在StringJoiner有个成员变量是emptyValue,若不指定,默认是 emptyValue = 前缀符+后缀符,而出来没有add过其他字符串,因此返回默认值emptyValue。

public class demo {
    public static void main(String[] args) {
    	// 指定默认值
        StringJoiner stringJoiner = new StringJoiner(",","kk","dd");
        stringJoiner.setEmptyValue("我是默认值");

        System.out.println(stringJoiner.toString());
        System.out.println(stringJoiner.length());
    }
}

在这里插入图片描述
发现指定了默认值,则不会返回前缀+后缀字符串了,因为重置了emptyValue值,返回固然是设置的值,长度更加不用多说吧
具体原因请看下面源码解析:

3. 源码

1. 成员变量

	// 前缀字符
	private final String prefix;
	// 分隔字符
    private final String delimiter;
    // 后缀字符
    private final String suffix;
    // 拼接结果,注类型是StringBuilder
    private StringBuilder value;
    // 默认值,当new StringJoiner时,没有add其他字符串
    // 此时toString,返回的就是emptyValue
    private String emptyValue;

2. 构造函数

// 指定分隔符,前后缀默认为""
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();
        // 此时 emptyValue是前后缀符的拼接
        this.emptyValue = this.prefix + this.suffix;
}

3. setEmptyValue()

public StringJoiner setEmptyValue(CharSequence emptyValue) {
		// 从上述构造函数可以看出,默认值是前后缀符的拼接
		// 还可以使用setEmptyValue 设置自定义默认值
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
}

4. add()

    public StringJoiner add(CharSequence newElement) {
    	// 添加字符串
        prepareBuilder().append(newElement);
        return this;
    }
	
	private StringBuilder prepareBuilder() {
        if (value != null) {
        	// 在添加新的String的时候,先添加分隔符
            value.append(delimiter);
        } else {
        	// 如果value还没有被构造,说明是第一次add
        	// 第一次add,当然要先new StringBuilder(),并且加前缀
        	// 也可以看出 value是懒加载,不用就不new
        	// 当然这样子做的原因也是为了区分是否第一次add,才能合理判断是加前缀,还是分隔符。
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

5. toString()

@Override
    public String toString() {
    	// 根据上面add()源码可以知道,只有第一次add了,才会new StringBuild
    	// 因此 value == null 说明从来没有add字符串过
        if (value == null) {
        	// 因此返回的是默认值
        	// 若没有setEmptyValue过,则返回前后缀字符串拼接的值
        	// 若设置过,则返回设置的值
            return emptyValue;
        } else {
        	// 走到这里,说明已经add过了
        	// 此时判断后缀是否为空串
            if (suffix.equals("")) {
                return value.toString();
            } else {
            	// 注意:这里是StringBuilder的length()方法
            	// 此时 value 里面有前缀,add的字符串,可能还有分隔符
            	// 此时的initialLength 长度没有算上后缀字符串
                int initialLength = value.length();
                // 现在将后缀加上,value.length()此时是5
                // 注意:此时的toString方法是StringBuilder类的
                String result = value.append(suffix).toString();
                // 这里却重置了,虽然 value里面包括后缀,但是长度没有算进去
                // 即 value.length()等于 实际长度减后缀符长度
                // 也就是 value里面不算上后缀符,本文最后再详细解析
                value.setLength(initialLength);
                return result;
            }
        }
    }

6. merge()

// 合并两个StringJoiner
public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other);
        if (other.value != null) {
        	// 根据上面toString解析,知道value.length() 是不包括后缀的长度
            final int length = other.value.length();
            // 先在原来的value 加上 分隔符
            StringBuilder builder = prepareBuilder();
            // 再把other的字符串 添加到value里面
            // 可以看出,排除了other的前缀符
            builder.append(other.value, other.prefix.length(), length);
        }
        return this;
    }

7. 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.
        // 调用StringJoiner的toString方法,此时算上后缀符长度
        // 若没有add过,则返回默认值的长度
        return (value != null ? value.length() + suffix.length() :
                emptyValue.length());
    }

8. 再次详细解释toString(),跟length()方法

上文中说到toString中有下面的逻辑:

			if (suffix.equals("")) {
                return value.toString();
            } else {
                int initialLength = value.length();
                String result = value.append(suffix).toString();
                value.setLength(initialLength);
                return result;
            }

首先要明确:value.length() 与 value.append(suffix).toString() 与 value.setLength(initialLength); 调用的是StringBuilder的方法。
在StringBuilder中的toString与setLength

    @Override
    public String toString() {
        // 它是new String,指定了范围的
        // 打个比方:如果value为ABC
        // new String(value,0,1) 则返回是A
        return new String(value, 0, count);
    }

	public void setLength(int newLength) {
        if (newLength < 0)
            throw new StringIndexOutOfBoundsException(newLength);
        ensureCapacityInternal(newLength);

        if (count < newLength) {
            Arrays.fill(value, count, newLength, '');
        }
		// count就是你设置的大小
        count = newLength;
    }

代码例子:

public class demo {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();
        builder.append("ABC");
        builder.setLength(1);
        System.out.println(builder.toString());
    }
}

在这里插入图片描述
发现 只返回A,实际上builder里面是ABC,但是重置了length即重置了count,因此BC相当于没有了。

重新回到这里StringJoiner的toString方法里面某个片段:

			if (suffix.equals("")) {
                return value.toString();
            } else {
                int initialLength = value.length();
                String result = value.append(suffix).toString();
                // value里面虽然在上一步加上了后缀字符串
                // 但是这里重置了length,相当于 value 没有加上后缀字符串
                value.setLength(initialLength);
                return result;
            }

因此在StringJoiner的length()方法里面才有 加上后缀长度的逻辑。
为什么要这样子设计了?
根据在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());
    }

翻译可以得:就是可以随时想添加新的字符串都可以。
代码:

public class demo {
    public static void main(String[] args) {
        StringJoiner stringJoiner = new StringJoiner(",", "A", "结束了");

        stringJoiner.add("B").add("C");
        System.out.println(stringJoiner.toString());
        System.out.println(stringJoiner.length());

        stringJoiner.add("D").add("F");
        System.out.println(stringJoiner.toString());
        System.out.println(stringJoiner.length());
    }
}

在这里插入图片描述
可以想一想,如果toString里面没有重置value(StringBuilder)的长度,那么你toString之后,岂不是再add的时候,应该是 AB,C结束了,D,F结束了,显然不是设计的初衷

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值