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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dancheng1/article/details/84820070

在通读整片源码前先来了解其中的两个内部类,这两个内部类才是真正去分解字符串的工人:

处理字符、字符串、正则的接口,此接口的定义实质为策略模式

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开始,找到第一个分隔符然后返回位置,没有找到返回-1
                public int separatorStart(int start) {
                    //获取分割符长度
                    int separatorLength = separator.length();
                    //记录分割符开始位子
                    int p = start;
                    //调用本类的 toSplit 变量即被分割的字符串长度,last取被分割的字符串长度与分割符的差值
                    //分割符号”,” 被分割字符串”a,b,c,d” last= 7-1 = 6
                    int 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值赋值给变量

猜你喜欢

转载自blog.csdn.net/dancheng1/article/details/84820070