StringJoiner拼接字符串(使用以及源码解析)
文章目录
- 1. 介绍
- 2. 代码演示
- 3. 源码
- 1. 成员变量
- 2. 构造函数
- 3. setEmptyValue()
- 4. add()
- 5. toString()
- 6. merge()
- 7. length()
- 8. 再次详细解释toString(),跟length()方法
1. 介绍
- StringJoiner类是jdk8推出的,用于构造由分隔符分隔的字符序列,并可选择性地从提供的前缀开始和以提供的后缀结尾。
- 说的通俗一点:就是让字符串之间有分隔符或者字符串有前缀或者后缀,不用开发人员自己拼接。
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拼接字符串(使用以及源码解析)相关推荐
- 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 ...
- FastJson PropertyNamingStrategy 转换成JSON字符串的四种格式配置及源码解析
FastJson在将bean转成JSON字符串时, 默认使用CamelCase(驼峰命名)的配置; 在1.2.15版本之后,FastJson支持配置PropertyNamingStrategy,有四种 ...
- Java String源码解析
String类概要 所有的字符串字面量都属于String类,String对象创建后不可改变,因此可以缓存共享,StringBuilder,StringBuffer是可变的实现 String类提供了操作 ...
- spring boot 源码解析23-actuate使用及EndPoint解析
前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...
- 【阅读笔记】Taro转小程序编译源码解析
前言 这篇文章的主要是对taro/taro-tarnsformer-wx进行源码解析,对于想要了解Taro或者了解babel的人希望看了能得到一定的启发. 由于我文笔实在太烂,所以整篇文章都是以阅读笔 ...
- Android Hawk数据库的源码解析,Github开源项目,基于SharedPreferences的的存储框架
今天看了朋友一个项目用到了Hawk,然后写了这边文章 一.了解一下概念 Android Hawk数据库github开源项目 Hawk是一个非常便捷的数据库.操作数据库只需一行代码,能存任何数据类型. ...
- 详细讲解go web框架之gin框架源码解析记录及思路流程和理解
开篇 首先gin 框架是在 官方提供的net/http标准包进行的相应封装. 那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识. 可以参考中文的 文档: https://st ...
- Android Hawk的源码解析,一款基于SharedPreferences的存储框架
转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...
- 【dubbo源码解析】 --- dubbo spi 机制(@SPI、@Adaptive)详解
本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外 文章目 ...
- SpringBoot入门-源码解析(雷神)
一.Spring Boot入门 视频学习资料(雷神): https://www.bilibili.com/video/BV19K4y1L7MT?p=1 github: https://github.c ...
最新文章
- bigint对应java什么类型_「JAVA」从格式化输出到扫描输入,深究Java正则表达式匹配之道
- ubuntu下硬盘相关
- PyTorch-运算加速
- DCL并非单例模式专用
- 【转载】interpolation(插值)和 extrapolation(外推)的区别
- 图灵奖大佬 Lecun 发表对比学习新作,比 SimCLR 更好用!
- DFB [02] 基础和相关的非常用的几个链接
- 微信小程序模板消息群发解决思路
- 使用Vue cli 来快速开发并打包封装项目教程
- Find Any File for Mac(本地文件搜索查找工具)
- MySQL配置文件my.cnf参数优化和中文详解
- Java:三角函数计算器!
- wine装通达信_【已解决】谁能给我一个可以运行通达信股票软件的wine
- 3 Idiots ——谢 阿米尔·汗
- 算法细节系列(3):梯度下降法,牛顿法,拟牛顿法
- java-php-net-python-绥化市北林区房屋拆迁管理信息管理系统计算机毕业设计程序
- Highly Efficient Salient Object Detection with 100K Parameters论文解读
- Python可视化分析疫情数据
- Day17 指针变量做函数形参 指针函数 函数指针
- java波斯王子武者之心,波斯王子武者之心
热门文章
- android 功能防抖,Android RxJava 实战系列:功能防抖
- 对公司的建议_工作总结
- NHANES数据库的介绍及使用(二)
- Rest-assured框架详解
- 工厂方法模式应用场景
- linux 分卷压缩及解压缩
- 汽车标志你认识多少?爱车族一定要了解哦!
- 新唐(nuvoton)MCU软件开发指南—环境搭建设置
- Linux 账号管理与 ACL 权限配置
- 【Codeforces Round #476 (Div. 2) [Thanks, Telegram!] B】Battleship