Guavaソース読解計画:base.Splitter

11800 ワード

ポリシーモード(Strategy)
一連のアルゴリズムを定義し、各アルゴリズムをカプセル化し、互いに置き換えることもできます.ポリシー・モードは、アルゴリズムの変化がアルゴリズムを使用するお客様に影響を与えないようにします.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();
  }
}