文章目录

  • 1. 介绍
  • 2. 代码演示
  • 3. 源码
    • 1. 成员变量
    • 2. 构造函数
    • 3. setEmptyValue()
    • 4. add()
    • 5. toString()
    • 6. merge()
    • 7. length()
    • 8. 再次详细解释toString(),跟length()方法

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;// 拼接结果,注类型是StringBuilderprivate StringBuilder value;// 默认值,当new StringJoiner时,没有add其他字符串// 此时toString,返回的就是emptyValueprivate 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()

@Overridepublic 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

    @Overridepublic String toString() {// 它是new String,指定了范围的// 打个比方:如果value为ABC// new String(value,0,1) 则返回是Areturn 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, '\0');}// 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结束了,显然不是设计的初衷

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

  1. Redis源码-String:Redis String命令、Redis String存储原理、Redis String三种编码类型、Redis字符串SDS源码解析、Redis String应用场景

    Redis源码-String:Redis String命令.Redis String存储原理.Redis String三种编码类型.Redis字符串SDS源码解析.Redis String应用场景 R ...

  2. FastJson PropertyNamingStrategy 转换成JSON字符串的四种格式配置及源码解析

    FastJson在将bean转成JSON字符串时, 默认使用CamelCase(驼峰命名)的配置; 在1.2.15版本之后,FastJson支持配置PropertyNamingStrategy,有四种 ...

  3. Java String源码解析

    String类概要 所有的字符串字面量都属于String类,String对象创建后不可改变,因此可以缓存共享,StringBuilder,StringBuffer是可变的实现 String类提供了操作 ...

  4. spring boot 源码解析23-actuate使用及EndPoint解析

    前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...

  5. 【阅读笔记】Taro转小程序编译源码解析

    前言 这篇文章的主要是对taro/taro-tarnsformer-wx进行源码解析,对于想要了解Taro或者了解babel的人希望看了能得到一定的启发. 由于我文笔实在太烂,所以整篇文章都是以阅读笔 ...

  6. Android Hawk数据库的源码解析,Github开源项目,基于SharedPreferences的的存储框架

    今天看了朋友一个项目用到了Hawk,然后写了这边文章 一.了解一下概念 Android Hawk数据库github开源项目 Hawk是一个非常便捷的数据库.操作数据库只需一行代码,能存任何数据类型. ...

  7. 详细讲解go web框架之gin框架源码解析记录及思路流程和理解

    开篇 首先gin 框架是在 官方提供的net/http标准包进行的相应封装. 那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识. 可以参考中文的 文档: https://st ...

  8. Android Hawk的源码解析,一款基于SharedPreferences的存储框架

    转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...

  9. 【dubbo源码解析】 --- dubbo spi 机制(@SPI、@Adaptive)详解

    本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外 文章目 ...

  10. SpringBoot入门-源码解析(雷神)

    一.Spring Boot入门 视频学习资料(雷神): https://www.bilibili.com/video/BV19K4y1L7MT?p=1 github: https://github.c ...

最新文章

  1. bigint对应java什么类型_「JAVA」从格式化输出到扫描输入,深究Java正则表达式匹配之道
  2. ubuntu下硬盘相关
  3. PyTorch-运算加速
  4. DCL并非单例模式专用
  5. 【转载】interpolation(插值)和 extrapolation(外推)的区别
  6. 图灵奖大佬 Lecun 发表对比学习新作,比 SimCLR 更好用!
  7. DFB [02] 基础和相关的非常用的几个链接
  8. 微信小程序模板消息群发解决思路
  9. 使用Vue cli 来快速开发并打包封装项目教程
  10. Find Any File for Mac(本地文件搜索查找工具)
  11. MySQL配置文件my.cnf参数优化和中文详解
  12. Java:三角函数计算器!
  13. wine装通达信_【已解决】谁能给我一个可以运行通达信股票软件的wine
  14. 3 Idiots ——谢 阿米尔·汗
  15. 算法细节系列(3):梯度下降法,牛顿法,拟牛顿法
  16. java-php-net-python-绥化市北林区房屋拆迁管理信息管理系统计算机毕业设计程序
  17. Highly Efficient Salient Object Detection with 100K Parameters论文解读
  18. Python可视化分析疫情数据
  19. Day17 指针变量做函数形参 指针函数 函数指针
  20. java波斯王子武者之心,波斯王子武者之心

热门文章

  1. android 功能防抖,Android RxJava 实战系列:功能防抖
  2. 对公司的建议_工作总结
  3. NHANES数据库的介绍及使用(二)
  4. Rest-assured框架详解
  5. 工厂方法模式应用场景
  6. linux 分卷压缩及解压缩
  7. 汽车标志你认识多少?爱车族一定要了解哦!
  8. 新唐(nuvoton)MCU软件开发指南—环境搭建设置
  9. Linux 账号管理与 ACL 权限配置
  10. 【Codeforces Round #476 (Div. 2) [Thanks, Telegram!] B】Battleship