失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Guava源码解析五:Splitter源码解析

Guava源码解析五:Splitter源码解析

时间:2022-06-17 19:16:04

相关推荐

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方法@GwtIncompatiblepublic static Splitter onPattern(String separatorPattern) {return on((CommonPattern)pilePattern(separatorPattern));}//传入一个Pattern类型参数,返回一个调用传入CommonPattern类型的on方法@GwtIncompatiblepublic 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源码解析》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
Guava  之 Splitter

Guava 之 Splitter

2022-01-01

Guava---Splitter

Guava---Splitter

2023-02-14

Guava入门~Splitter

Guava入门~Splitter

2023-11-02

Google Guava Splitter

Google Guava Splitter

2019-09-01