Guava源码解析五:Splitter源码解析
在通读整片源码前先来了解其中的两个内部类,这两个内部类才是真正去分解字符串的工人:
处理字符、字符串、正则的接口,此接口的定义实质为策略模式
private interface Strategy {Iterator<String> iterator(Splitter var1, CharSequence var2);
}
此接口中只有一个方法,返回的是一个Iterator迭代器,这里我们可以先联想到最终返回的集合的迭代器会与它有关系
这里实现了一个惰性迭代器,直到不得不计算的时候才会去将字符串分割,即在迭代的时候才去分割字符串,无论将分隔符还是被分割的字符串加载到Splitter类中,都不会去分割,只有在迭代的时候才会真正的去分割
private abstract static class SplittingIterator extends AbstractIterator<String> {final CharSequence toSplit;final CharMatcher trimmer;final boolean omitEmptyStrings;int offset = 0;int limit;//获取被分割字符串中第一个与分隔符匹配的位置abstract int separatorStart(int var1);//获取当前分隔符在字符串中的结尾位置abstract int separatorEnd(int var1);//将当前的截取字符串信息赋值给SplittingIterator变量protected SplittingIterator(Splitter splitter, CharSequence toSplit) {this.trimmer = splitter.trimmer;this.omitEmptyStrings = splitter.omitEmptyStrings;this.limit = splitter.limit;this.toSplit = toSplit;}//重写迭代方法,就是这里实现的懒迭代器protected String computeNext() {int nextStart = this.offset;while(true) {while(this.offset != -1) {int start = nextStart;//根据separatorStart方法进行获取字符串中的第一个分隔符位置int separatorPosition = this.separatorStart(this.offset);int end;if(separatorPosition == -1) {end = this.toSplit.length();this.offset = -1;} else {end = separatorPosition;//根据separatorEnd方法进行获取字符串中的第一个分隔符的结束位置this.offset = this.separatorEnd(separatorPosition);}if(this.offset != nextStart) {while(start < end && this.trimmer.matches(this.toSplit.charAt(start))) {++start;}while(end > start && this.trimmer.matches(this.toSplit.charAt(end - 1))) {--end;}//如果omitEmptyStrings为true,则对空结果跳过处理if(!this.omitEmptyStrings || start != end) {//当规定的最多结果数值为1时,输出最后的所有字符串,然后结束迭代if(this.limit == 1) {end = this.toSplit.length();for(this.offset = -1; end > start && this.trimmer.matches(this.toSplit.charAt(end - 1)); --end) {;}} else {//没有到最后一个时,进行减1操作--this.limit;}return this.toSplit.subSequence(start, end).toString();}nextStart = this.offset;} else {++this.offset;if(this.offset > this.toSplit.length()) {this.offset = -1;}}}return (String)this.endOfData();}}
}
这是一个实现AbstractIterator的一个抽象类,他实现了 computeNext方法(此方法可以在看集合源码的时候多注意一下),这个方法实际上是规定了此迭代器的一个迭代规则。所以Splitter类为他分割完的结果集也写了一个迭代器并规定了自己的迭代规则。从这个迭代器的实现上,在结合Strategy 类便可以讲整个字符串分割的过程给串起来了
变量
//移除指定字符项,即集合中当前元素与trimmer匹配,将其移除。如果没有设置trimmer,则将结果中的空格删除
//最终结论为:将结果集中的每个字符串前缀和后缀都去除trimmer,知道前缀或后缀没有这个字符了,字符串“中”的不用去除
private final CharMatcher trimmer;
//是否移除结果集中的空集,true为移除结果集中的空集,false为不用移除结果集中的空集
private final boolean omitEmptyStrings;
//这个变量最终会返回一个所有集合类的父接口,它是贯穿着整个字符串分解的变量
private final Splitter.Strategy strategy;
//最多将字符串分为几个集合,比如limit=3, 对”a,b,c,d”字符串进行','分割,返回的[”a”,”b”,”c,d”] 意思为最多可以分割成3段,这个可以在链式编程的limit方法参数设置
private final int limit;
构造方法
两个构造函数都是静态构造器,所以不能直接使用这两个构造器去创建Splitter,想要创建Splitter只能使用静态方法。
//接收 一个Strategy类对象
private Splitter(Splitter.Strategy strategy) {this(strategy, false, CharMatcher.NONE, 2147483647);
}//此构造器为所有变量进行赋值
private Splitter(Splitter.Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) {this.strategy = strategy;this.omitEmptyStrings = omitEmptyStrings;this.trimmer = trimmer;this.limit = limit;
}
静态创建Splitter函数
静态创建Splitter函数可以按照4类进行分析(接收字符、字符串、正则表达式的和按指定长度分割构造器):
第一种:接收字符的构造器
//接收一个字符的构造器,然后调用参数为 CharMatcher的构造器
public static Splitter on(char separator) {return on((CharMatcher)CharMatcher.is(separator));
}//接收一个CharMatcher的构造器
public static Splitter on(final CharMatcher separatorMatcher) {//对字符判空Preconditions.checkNotNull(separatorMatcher);//返回一个Splitter对象,传入Strategy对象,并对Strategy接口进行实现return new Splitter(new Splitter.Strategy() {//实现接口Strategy的iterator方法public Splitter.SplittingIterator iterator(final Splitter splitter, final CharSequence toSplit) {//返回 SplittingIterator对象,并对 SplittingIterator 抽象类实现 separatorStart方法和 separatorEnd方法return new Splitter.SplittingIterator(splitter, toSplit) {//返回从start开始的第一个分隔符的开始位置int separatorStart(int start) {return separatorMatcher.indexIn(this.toSplit, start);}//返回当前分割符的末尾位置int separatorEnd(int separatorPosition) {return separatorPosition + 1;}};}});
}
第二种:接收字符串构造器
//传入一个字符串作为分隔符
public static Splitter on(final String separator) {Preconditions.checkArgument(separator.length() != 0, "The separator may not be the empty string.");//如果当前字符串的长度为1,则直接调用解析单个字符的构造器上,否则会返回一个Splitter对象,传入Strategy对象,并对Strategy接口进行实现return separator.length() == 1?on(separator.charAt(0)):new Splitter(new Splitter.Strategy() {//实现Strategy接口public Splitter.SplittingIterator iterator(final Splitter splitter, final CharSequence toSplit) {return new Splitter.SplittingIterator(splitter, toSplit) {//这个方法是被分割字符串从start开始,找到第一个分隔符然后返回位置,没有找到返回-1public int separatorStart(int start) {//获取分割符长度int separatorLength = separator.length();//记录分割符开始位子int p = start;//调用本类的 toSplit 变量即被分割的字符串长度,last取被分割的字符串长度与分割符的差值//分割符号”,” 被分割字符串”a,b,c,d” last= 7-1 = 6int last = this.toSplit.length() - separatorLength;//找到匹配到分隔符的第一个位置label23:while(p <= last) {for(int i = 0; i < separatorLength; ++i) {if(this.toSplit.charAt(i + p) != separator.charAt(i)) {++p;continue label23;}}return p;}return -1;}//传入分离器位置,返回分离器末尾位置public int separatorEnd(int separatorPosition) {return separatorPosition + separator.length();}};}});
}
第三种:接收正则表达式构造器
//传入一个字符串,返回一个调用传入CommonPattern类型的on方法
@GwtIncompatible
public static Splitter onPattern(String separatorPattern) {return on((CommonPattern)Platform.compilePattern(separatorPattern));
}//传入一个Pattern类型参数,返回一个调用传入CommonPattern类型的on方法
@GwtIncompatible
public static Splitter on(Pattern separatorPattern) {return on((CommonPattern)(new JdkPattern(separatorPattern)));
}//传入一个 CommonPattern类型的构造器
private static Splitter on(final CommonPattern separatorPattern) {Preconditions.checkArgument(!separatorPattern.matcher("").matches(), "The pattern may not match the empty string: %s", separatorPattern);//返回一个Splitter对象,传入Strategy对象,并对Strategy接口进行实现return new Splitter(new Splitter.Strategy() {//实现Strategy对象的iterator方法public Splitter.SplittingIterator iterator(final Splitter splitter, final CharSequence toSplit) {final CommonMatcher matcher = separatorPattern.matcher(toSplit);return new Splitter.SplittingIterator(splitter, toSplit) {//返回从start开始的第一个分隔符的开始位置public int separatorStart(int start) {return matcher.find(start)?matcher.start():-1;}//返回当前分割符的末尾位置public int separatorEnd(int separatorPosition) {return matcher.end();}};}});
}
第四种:按指定长度分割的构造器
public static Splitter fixedLength(final int length) {Preconditions.checkArgument(length > 0, "The length may not be less than 1");return new Splitter(new Splitter.Strategy() {public Splitter.SplittingIterator iterator(final Splitter splitter, final CharSequence toSplit) {return new Splitter.SplittingIterator(splitter, toSplit) {//按 length长度进行跨步public int separatorStart(int start) {int nextChunkStart = start + length;return nextChunkStart < this.toSplit.length()?nextChunkStart:-1;}public int separatorEnd(int separatorPosition) {return separatorPosition;}};}});
}
进行分割的函数 (split、splittingIterator)
对分割完字符串后的存储结构可以分为返回值为容器的和List的,分别介绍这两个方法
首先介绍返回值为容器的
public Iterable<String> split(final CharSequence sequence) {Preconditions.checkNotNull(sequence);//返回一个容器,然后重写了iterator和toString方法return new Iterable() {public Iterator<String> iterator() {//调用了 splittingIterator方法,以下可以查看 splittingIterator方法的实现return Splitter.this.splittingIterator(sequence);}public String toString() {return Joiner.on(", ").appendTo((new StringBuilder()).append('['), this).append(']').toString();}};
}
以下是 splittingIterator的源码
private Iterator<String> splittingIterator(CharSequence sequence) {return this.strategy.iterator(this, sequence);
}
这里调用了Strategy的iterator方法,这个方法在on里面有多种的实现方法,所以结合最先的 SplittingIterator类重写的迭代方法,这里就形成了一个特殊的容器返回,真正的拆分字符串动作是在迭代的时候进行的。
在对返回List对象的方法进行解析
public List<String> splitToList(CharSequence sequence) {Preconditions.checkNotNull(sequence);Iterator iterator = this.splittingIterator(sequence);ArrayList result = new ArrayList();while(iterator.hasNext()) {result.add(iterator.next());}return Collections.unmodifiableList(result);
}
实际上他和上面一样调用了Strategy和SplittingIterator中的方法实现了在迭代时候去分割字符串。只不过他真正的迭代了一边然后将结果集放在了List容器中,与直接抛出一个可迭代的容器各有各的好吧。
其他功能函数
omitEmptyStrings方法
移去结果中的空字符串根据源码说明:
public Splitter omitEmptyStrings() {return new Splitter(this.strategy, true, this.trimmer, this.limit);
}
这里就是将omitEmptyStrings标记位改为true,在进行输出操作时将空结果略过
trimResults方法
他有两种实现方法:
//将结果中的空格删除
public Splitter trimResults() {return this.trimResults(CharMatcher.whitespace());
}//移除指定字符
public Splitter trimResults(CharMatcher trimmer) {Preconditions.checkNotNull(trimmer);return new Splitter(this.strategy, this.omitEmptyStrings, trimmer, this.limit);
}
调用此方法可以将结果集中的每个字符串前缀和后缀都去除trimmer,他的实现也是在迭代器中进行的
limit方法
达到指定数目后停止字符串划分
public Splitter limit(int limit) {Preconditions.checkArgument(limit > 0, "must be greater than zero: %s", limit);return new Splitter(this.strategy, this.omitEmptyStrings, this.trimmer, limit);
}
将传入的limit值赋值给变量
Guava源码解析五:Splitter源码解析相关推荐
- Mybatis-Spring源码分析(五) MapperMethod和MappedStatement解析
前言 基本上这就是Mybatis-Spring源码的最后一篇了,如果想起来什么再单开博客.比起来Spring源码,Mybatis的确实简单一些,本篇就说一下Mybatis中两个十分重要的类Mapper ...
- java解析五元组_pcap文件解析,并且按照五元组分类
[实例简介] pcap文件解析,并按照五元组分包,全部用java语言实现. [实例截图] [核心代码] PcapTestZZ ├── PcapTestZ │ ├── 111.206.37.1930 ...
- cartographer 源码解析 (五)
相关链接: cartographer 源码解析(一) cartographer 源码解析(二) cartographer 源码解析(三) cartographer 源码解析(四) cartograph ...
- (Nacos源码解析五)Nacos服务事件变动源码解析
Nacos源码解析系列目录 Nacos 源码编译运行 (Nacos源码解析一)Nacos 注册实例源码解析 (Nacos源码解析二)Nacos 服务发现源码解析 (Nacos源码解析三)Nacos 心 ...
- Spring源码深度解析,Spring源码以及Bean的生命周期(五)(附代码示例:)
五)Bean 的生命周期,创建---初始化---销毁的过程 目录 五)Bean 的生命周期,创建---初始化---销毁的过程 一 , 指定初始化方法 init-method 方法 二 ,指定销毁 ...
- Tomcat源码解析五:Tomcat请求处理过程
前面已经分析完了Tomcat的启动和关闭过程,本篇就来接着分析一下Tomcat中请求的处理过程. 在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求-> ...
- 源码解析:Spring源码解析笔记(五)接口设计总览
本文由colodoo(纸伞)整理 QQ 425343603 Java学习交流群(717726984) Spring解析笔记 启动过程部分已经完成,对启动过程源码有兴趣的朋友可以作为参考文章. 源码解析 ...
- SpringBoot各类型参数解析原理(源码)
上次那篇我们只分析了doDispatch中的getHandler方法(获取执行链,执行链里包括当前请求URL对应的 handler 以及拦截器(Controller.method绑定关系)),今儿继续 ...
- Spring源码解析:自定义标签的解析过程
2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...
- mybatis源码阅读(五) ---执行器Executor
转载自 mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_ ...
最新文章
- gdi 中发生一般性错误_SMC/SMD波峰焊接过程中的注意事项分析
- 【论文】基于特定实体的文本情感分类总结(PART III)
- React Native – 使用 JavaScript 开发原生应用
- 如何使用数据卷在宿主机和docker容器之间共享文件
- 用 pyecharts 制作数据可视化大屏之数据地图
- lombok报错Ambiguous method call. Both
- 网络模块与RJ45水晶头接线方法
- HTML内嵌内联外联——它们之间的优先级如何?
- arch Linux更新添加源,Arch Linux 更新源(以清华 arch 源为例)
- 阿里云ECS服务器配置全攻略
- matlab三相触发电路图,三相过零触发电路图
- MATLAB 2016a系统错误解决方案
- 北京车牌那么难摇为什么还能那么受欢迎?
- html中怎么设置渐变颜色设置,css中渐变色怎么设置
- CMN_1022: [FreeTDS][SQL Server]The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION
- Android中添加思源字体/NotoSansCJK/SourceHanSans
- winbox添加dhcp和nat
- matlab_矩阵和数组
- 设计模式-简单工厂模式(SimpleFactory)
- 解压缩文件:压缩(zipped)文件夹错误,拒绝访问