Guavaソース読解計画:base.Splitter
11800 ワード
ポリシーモード(Strategy)
一連のアルゴリズムを定義し、各アルゴリズムをカプセル化し、互いに置き換えることもできます.ポリシー・モードは、アルゴリズムの変化がアルゴリズムを使用するお客様に影響を与えないようにします.Splitterでは主にこのパターンが用いられている.
主な方法
基本的な書き方は次のとおりです.
第1部構築
Splitter.on( )
Splitter.がたくさんありますon()メソッドは各パラメータが異なるが,Spliterオブジェクトを構築している.
WIKIはここで戦略モデルを体現しており、異なるSplitter.on()は互いに置き換えることができる
例:
Splitterの構築方法
Strategyインタフェース
SplittingIteratorクラス
Strategyインタフェースメソッドの実装ではSplittingIteratorクラスが構築されています
パート2呼び出し
split( )
split()メソッドを呼び出して分割
WIKIインタフェースメソッドは、SplittingIteratorオブジェクトがAbstractIteratorに継承されていることを返します.
これまで分割操作は行われておらず,最終的にAbstractIteratorクラスの実装が得られた.
しかし、どのように分割しますか?
WIKIはその後、computeNext()メソッドを呼び出してから本当に分割します.
splitToList()
これはもう一つの実装がリストを返すものである
Map split()
戻り値Mapのメソッド
第3部追加の設定
これらの条件の戻り値をonメソッドと同様にSplitterオブジェクトであることを選択できる条件はたくさんあります.Splitterの値を設定したことに等しいです.
もちろんmapに切り分ける場合はMapSplitterクラスオブジェクトを返すこともあるが,属性はやはり2つのSplitterオブジェクトがmapを構成する2つの条件で2回分割される.
拡張
AbstractIteratorクラス
ここのAbstractIteratorクラスはcomです.google.common.baseが包んだのはcomです.google.common.collect.AbstractIteratorの略語
一連のアルゴリズムを定義し、各アルゴリズムをカプセル化し、互いに置き換えることもできます.ポリシー・モードは、アルゴリズムの変化がアルゴリズムを使用するお客様に影響を与えないようにします.Splitterでは主にこのパターンが用いられている.
主な方法
基本的な書き方は次のとおりです.
Splitter.on('.').split("a,b,c");
第1部構築
Splitter.on( )
Splitter.がたくさんありますon()メソッドは各パラメータが異なるが,Spliterオブジェクトを構築している.
WIKIはここで戦略モデルを体現しており、異なるSplitter.on()は互いに置き換えることができる
例:
public static Splitter on(final String separator) {
checkArgument(separator.length() != 0, "The separator may not be the empty string.");
if (separator.length() == 1) {
return Splitter.on(separator.charAt(0));
}
// Splitter
return new Splitter(
// Strategy ,
new Strategy() {
@Override
public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) {
// SplittingIterator
return new SplittingIterator(splitter, toSplit) {
@Override
public int separatorStart(int start) {
int separatorLength = separator.length();
positions:
for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) {
for (int i = 0; i < separatorLength; i++) {
if (toSplit.charAt(i + p) != separator.charAt(i)) {
continue positions;
}
}
return p;
}
return -1;
}
@Override
public int separatorEnd(int separatorPosition) {
return separatorPosition + separator.length();
}
};
}
});
}
Splitterの構築方法
// , Strategy
private Splitter(Strategy strategy) {
this(strategy, false, CharMatcher.none(), Integer.MAX_VALUE);
}
// , Strategy, , CharMatcher ,
private Splitter(Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) {
this.strategy = strategy;
this.omitEmptyStrings = omitEmptyStrings;
this.trimmer = trimmer;
this.limit = limit;
}
Strategyインタフェース
private interface Strategy {
// Iterator
Iterator iterator(Splitter splitter, CharSequence toSplit);
}
SplittingIteratorクラス
Strategyインタフェースメソッドの実装ではSplittingIteratorクラスが構築されています
private abstract static class SplittingIterator extends AbstractIterator {
final CharSequence toSplit;
final CharMatcher trimmer;
final boolean omitEmptyStrings;
/**
* Returns the first index in {@code toSplit} at or after {@code start} that contains the
* separator.
*/
abstract int separatorStart(int start);
/**
* Returns the first index in {@code toSplit} after {@code separatorPosition} that does not
* contain a separator. This method is only invoked after a call to {@code separatorStart}.
*/
abstract int separatorEnd(int separatorPosition);
int offset = 0;
int limit;
protected SplittingIterator(Splitter splitter, CharSequence toSplit) {
this.trimmer = splitter.trimmer;
this.omitEmptyStrings = splitter.omitEmptyStrings;
this.limit = splitter.limit;
this.toSplit = toSplit;
}
}
パート2呼び出し
split( )
split()メソッドを呼び出して分割
public Iterable split(final CharSequence sequence) {
checkNotNull(sequence);
return new Iterable() {
@Override
public Iterator iterator() {
return splittingIterator(sequence);
}
@Override
public String toString() {
return Joiner.on(", ")
.appendTo(new StringBuilder().append('['), this)
.append(']')
.toString();
}
};
}
private Iterator splittingIterator(CharSequence sequence) {
//
return strategy.iterator(this, sequence);
}
WIKIインタフェースメソッドは、SplittingIteratorオブジェクトがAbstractIteratorに継承されていることを返します.
これまで分割操作は行われておらず,最終的にAbstractIteratorクラスの実装が得られた.
しかし、どのように分割しますか?
// AbstractIterator computeNext()
@Override
protected String computeNext() {
/*
* The returned string will be from the end of the last match to the beginning of the next
* one. nextStart is the start position of the returned substring, while offset is the place
* to start looking for a separator.
*/
int nextStart = offset;
while (offset != -1) {
int start = nextStart;
int end;
int separatorPosition = separatorStart(offset);
if (separatorPosition == -1) {
end = toSplit.length();
offset = -1;
} else {
end = separatorPosition;
offset = separatorEnd(separatorPosition);
}
if (offset == nextStart) {
/*
* This occurs when some pattern has an empty match, even if it doesn't match the empty
* string -- for example, if it requires lookahead or the like. The offset must be
* increased to look for separators beyond this point, without changing the start position
* of the next returned substring -- so nextStart stays the same.
*/
offset++;
if (offset > toSplit.length()) {
offset = -1;
}
continue;
}
while (start < end && trimmer.matches(toSplit.charAt(start))) {
start++;
}
while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
end--;
}
if (omitEmptyStrings && start == end) {
// Don't include the (unused) separator in next split string.
nextStart = offset;
continue;
}
if (limit == 1) {
// The limit has been reached, return the rest of the string as the
// final item. This is tested after empty string removal so that
// empty strings do not count towards the limit.
end = toSplit.length();
offset = -1;
// Since we may have changed the end, we need to trim it again.
while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
end--;
}
} else {
limit--;
}
return toSplit.subSequence(start, end).toString();
}
return endOfData();
}
}
WIKIはその後、computeNext()メソッドを呼び出してから本当に分割します.
splitToList()
これはもう一つの実装がリストを返すものである
public List splitToList(CharSequence sequence) {
checkNotNull(sequence);
Iterator iterator = splittingIterator(sequence);
List result = new ArrayList<>();
while (iterator.hasNext()) {
result.add(iterator.next());
}
//
return Collections.unmodifiableList(result);
}
Map split()
戻り値Mapのメソッド
public Map split(CharSequence sequence) {
Map map = new LinkedHashMap<>();
for (String entry : outerSplitter.split(sequence)) {
Iterator 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);
}
}
第3部追加の設定
これらの条件の戻り値をonメソッドと同様にSplitterオブジェクトであることを選択できる条件はたくさんあります.Splitterの値を設定したことに等しいです.
// omitEmptyString ,
public Splitter omitEmptyStrings() {
return new Splitter(strategy, true, trimmer, limit);
}
//
public Splitter limit(int limit) {
checkArgument(limit > 0, "must be greater than zero: %s", limit);
return new Splitter(strategy, omitEmptyStrings, trimmer, limit);
}
//
public Splitter trimResults() {
return trimResults(CharMatcher.whitespace());
}
// TODO(kevinb): throw if a trimmer was already specified!
public Splitter trimResults(CharMatcher trimmer) {
checkNotNull(trimmer);
return new Splitter(strategy, omitEmptyStrings, trimmer, limit);
}
もちろんmapに切り分ける場合はMapSplitterクラスオブジェクトを返すこともあるが,属性はやはり2つのSplitterオブジェクトがmapを構成する2つの条件で2回分割される.
public static final class MapSplitter {
private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry";
// Splitter
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 split(CharSequence sequence) {
Map map = new LinkedHashMap<>();
// Splitter
for (String entry : outerSplitter.split(sequence)) {
//
Iterator 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);
}
}
拡張
AbstractIteratorクラス
ここのAbstractIteratorクラスはcomです.google.common.baseが包んだのはcomです.google.common.collect.AbstractIteratorの略語
/**
* Note this class is a copy of {@link com.google.common.collect.AbstractIterator} (for dependency
* reasons).
*/
@GwtCompatible
abstract class AbstractIterator implements Iterator {
private State state = State.NOT_READY;
protected AbstractIterator() {}
private enum State {
READY,
NOT_READY,
DONE,
FAILED,
}
private @Nullable T next;
// splitter
protected abstract T computeNext();
@CanIgnoreReturnValue
protected final @Nullable T endOfData() {
state = State.DONE;
return null;
}
@Override
public final boolean hasNext() {
checkState(state != State.FAILED);
switch (state) {
case READY:
return true;
case DONE:
return false;
default:
}
return tryToComputeNext();
}
private boolean tryToComputeNext() {
state = State.FAILED; // temporary pessimism
next = computeNext();
if (state != State.DONE) {
state = State.READY;
return true;
}
return false;
}
@Override
public final T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
state = State.NOT_READY;
T result = next;
next = null;
return result;
}
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
}