Splitter
Java提供的字符串分割方法split,split源码分析如下。
public String[] split(String regex, int limit) {
char ch = 0;
// 1. 如果regex只有一位且regex不是列出来的这些特殊字符".$|()[{^?*+\\"
// 2. 如果regex只有两位且第一位为转义字符,第二位字符小于0或者大于9并且小于a或者大于z并且小于A或者大于Z
// 3. 与编码相关
if (((regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE)) {
int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else { // last one
//assert (list.size() == limit - 1);
list.add(substring(off, value.length));
off = value.length;
break;
}
}
// If no match was found, return this
if (off == 0)
return new String[]{this};
// Add remaining segment
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
// Construct result
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
Splitter
Java split方法会忽略掉空内容,但是Guava Splitter不会忽略,也可以进行选择性的忽略。
使用
- 构造方法
同Joinner,构造方法私有,使用静态方法返回Splitter对象。
// 私有构造方法
private Splitter(Strategy strategy) {
this(strategy, false, CharMatcher.none(), Integer.MAX_VALUE);
}
private Splitter(Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) {
this.strategy = strategy;
this.omitEmptyStrings = omitEmptyStrings;
this.trimmer = trimmer;
this.limit = limit;
}
// 静态方法
// 使用指定字符进行分割
public static Splitter on(char separator)
// 使用指定类进行分割
public static Splitter on(final CharMatcher separatorMatcher)
// 使用指定字符串进行分割
public static Splitter on(final String separator)
// 使用指定正则表达式进行分割
public static Splitter on(Pattern separatorPattern) {
return on(new JdkPattern(separatorPattern));
}
private static Splitter on(final CommonPattern separatorPattern)
Splitter 的实现中有十分明显的策略模式和模板模式,有各种神乎其技的方法覆盖,还有 Guava 久负盛名的迭代技巧和惰性计算。Spliter具有四个成员属性,具体如下。
// 用于描述分割之后内容前后指定字符的策略,例如trimResults将策略定为分割之后结果前后空格删除
private final CharMatcher trimmer;
// 是否删除空字符串
private final boolean omitEmptyStrings;
// 拆分之后的结果个数
private final int limit;
// 实现的策略模式
private final Strategy strategy;
Splitter的策略模式是一个接口,只有一个iterator方法,传入Splitter对象和待拆分的字符串,返回一个字符串的迭代器。
private interface Strategy {
Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
}
- 分割方法
split方法,对sequence进行分割,返回一个字符串的迭代器。
public Iterable<String> split(final CharSequence sequence) {
checkNotNull(sequence);
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return splittingIterator(sequence);
}
@Override
public String toString() {
return Joiner.on(", ")
.appendTo(new StringBuilder().append('['), this)
.append(']')
.toString();
}
};
}
private Iterator<String> splittingIterator(CharSequence sequence) {
return strategy.iterator(this, sequence);
}
splitToList方法,对sequence进行分割,返回List。
@Beta
public List<String> splitToList(CharSequence sequence) {
checkNotNull(sequence);
Iterator<String> iterator = splittingIterator(sequence);
List<String> result = new ArrayList<String>();
// 这里采用惰性求值的技巧,在最后调用splitToList时才将结果添加到result中
while (iterator.hasNext()) {
result.add(iterator.next());
}
return Collections.unmodifiableList(result);
}
- 其他方法
omitEmptyStrings方法会去除结果中的空字符串。
public Splitter omitEmptyStrings() {
return new Splitter(strategy, true, trimmer, limit);
}
trimResults方法会去除元素中的左右空字符串,内容中间的空字符串不会去除。
public Splitter trimResults() {
return trimResults(CharMatcher.whitespace());
}
public Splitter trimResults(CharMatcher trimmer) {
checkNotNull(trimmer);
return new Splitter(strategy, omitEmptyStrings, trimmer, limit);
}
fixedLength方法,按子字符串字符数量进行分割。
public static Splitter fixedLength(final int length) {
checkArgument(length > 0, "The length may not be less than 1");
return new Splitter(
new Strategy() {
@Override
public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
return new SplittingIterator(splitter, toSplit) {
@Override
public int separatorStart(int start) {
int nextChunkStart = start + length;
return (nextChunkStart < toSplit.length() ? nextChunkStart : -1);
}
@Override
public int separatorEnd(int separatorPosition) {
return separatorPosition;
}
};
}
});
}
limit方法,最多分割limit个子字符串。
public Splitter limit(int limit) {
checkArgument(limit > 0, "must be greater than zero: %s", limit);
return new Splitter(strategy, omitEmptyStrings, trimmer, limit);
}
onPattern方法,按正则表达式进行分割。
@GwtIncompatible // java.util.regex
public static Splitter onPattern(String separatorPattern) {
// 返回按正则表达式进行分割的Splitter
return on(Platform.compilePattern(separatorPattern));
}
withKeyValueSeparator方法,按照指定的分隔符将字符串分割为map。
public MapSplitter withKeyValueSeparator(String separator) {
return withKeyValueSeparator(on(separator));
}
@Beta
public MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) {
return new MapSplitter(this, keyValueSplitter);
}
// MapSplitter是Sppiter中的一个内部类,内部覆写了split方法
@Beta
public static final class MapSplitter {
private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry";
private final Splitter outerSplitter;
private final Splitter entrySplitter;
private MapSplitter(Splitter outerSplitter, Splitter entrySplitter) {
this.outerSplitter = outerSplitter; // only "this" is passed
this.entrySplitter = checkNotNull(entrySplitter);
}
public Map<String, String> split(CharSequence sequence) {
Map<String, String> map = new LinkedHashMap<String, String>();
for (String entry : outerSplitter.split(sequence)) {
Iterator<String> entryFields =
entrySplitter.splittingIterator(entry);
checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
String key = entryFields.next();
checkArgument(!map.containsKey(key), "Duplicate key [%s] found.", key);
checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
String value = entryFields.next();
map.put(key, value);
checkArgument(!entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
}
return Collections.unmodifiableMap(map);
}
}