guava/src/com/google/common/collect/CollectSpliterators.java
static <F, T> Spliterator<T> flatMap(
      Spliterator<F> fromSpliterator,
      Function<? super F, Spliterator<T>> function,
      int topCharacteristics,
      long topSize) {
    checkArgument(
        (topCharacteristics & Spliterator.SUBSIZED) == 0,
        "flatMap does not support SUBSIZED characteristic");
    checkArgument(
        (topCharacteristics & Spliterator.SORTED) == 0,
        "flatMap does not support SORTED characteristic");
    checkNotNull(fromSpliterator);
    checkNotNull(function);
    class FlatMapSpliterator implements Spliterator<T> {
      @Nullable Spliterator<T> prefix;
      final Spliterator<F> from;
      int characteristics;
      long estimatedSize;

      FlatMapSpliterator(
          Spliterator<T> prefix, Spliterator<F> from, int characteristics, long estimatedSize) {
        this.prefix = prefix;
        this.from = from;
        this.characteristics = characteristics;
        this.estimatedSize = estimatedSize;
      }

      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
        while (true) {
          if (prefix != null && prefix.tryAdvance(action)) {
            if (estimatedSize != Long.MAX_VALUE) {
              estimatedSize--;
            }
            return true;
          } else {
            prefix = null;
          }
          if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) {
            return false;
          }
        }
      }

      @Override
      public void forEachRemaining(Consumer<? super T> action) {
        if (prefix != null) {
          prefix.forEachRemaining(action);
          prefix = null;
        }
        from.forEachRemaining(fromElement -> function.apply(fromElement).forEachRemaining(action));
        estimatedSize = 0;
      }

      @Override
      public Spliterator<T> trySplit() {
        Spliterator<F> fromSplit = from.trySplit();
        if (fromSplit != null) {
          int splitCharacteristics = characteristics & ~Spliterator.SIZED;
          long estSplitSize = estimateSize();
          if (estSplitSize < Long.MAX_VALUE) {
            estSplitSize /= 2;
            this.estimatedSize -= estSplitSize;
            this.characteristics = splitCharacteristics;
          }
          Spliterator<T> result =
              new FlatMapSpliterator(this.prefix, fromSplit, splitCharacteristics, estSplitSize);
          this.prefix = null;
          return result;
        } else if (prefix != null) {
          Spliterator<T> result = prefix;
          this.prefix = null;
          return result;
        } else {
          return null;
        }
      }

      @Override
      public long estimateSize() {
        if (prefix != null) {
          estimatedSize = Math.max(estimatedSize, prefix.estimateSize());
        }
        return Math.max(estimatedSize, 0);
      }

      @Override
      public int characteristics() {
        return characteristics;
      }
    }
    return new FlatMapSpliterator(null, fromSpliterator, topCharacteristics, topSize);
  }
static <F, T> Spliterator<T> flatMap(
      Spliterator<F> fromSpliterator,
      Function<? super F, Spliterator<T>> function,
      int topCharacteristics,
      long topSize) {
    checkArgument(
        (topCharacteristics & Spliterator.SUBSIZED) == 0,
        "flatMap does not support SUBSIZED characteristic");
    checkArgument(
        (topCharacteristics & Spliterator.SORTED) == 0,
        "flatMap does not support SORTED characteristic");
    checkNotNull(fromSpliterator);
    checkNotNull(function);
    class FlatMapSpliterator implements Spliterator<T> {
      @Nullable Spliterator<T> prefix;
      final Spliterator<F> from;
      int characteristics;
      long estimatedSize;

      FlatMapSpliterator(
          Spliterator<T> prefix, Spliterator<F> from, int characteristics, long estimatedSize) {
        this.prefix = prefix;
        this.from = from;
        this.characteristics = characteristics;
        this.estimatedSize = estimatedSize;
      }

      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
        while (true) {
          if (prefix != null && prefix.tryAdvance(action)) {
            if (estimatedSize != Long.MAX_VALUE) {
              estimatedSize--;
            }
            return true;
          } else {
            prefix = null;
          }
          if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) {
            return false;
          }
        }
      }

      @Override
      public void forEachRemaining(Consumer<? super T> action) {
        if (prefix != null) {
          prefix.forEachRemaining(action);
          prefix = null;
        }
        from.forEachRemaining(fromElement -> function.apply(fromElement).forEachRemaining(action));
        estimatedSize = 0;
      }

      @Override
      public Spliterator<T> trySplit() {
        Spliterator<F> fromSplit = from.trySplit();
        if (fromSplit != null) {
          int splitCharacteristics = characteristics & ~Spliterator.SIZED;
          long estSplitSize = estimateSize();
          if (estSplitSize < Long.MAX_VALUE) {
            estSplitSize /= 2;
            this.estimatedSize -= estSplitSize;
            this.characteristics = splitCharacteristics;
          }
          Spliterator<T> result =
              new FlatMapSpliterator(this.prefix, fromSplit, splitCharacteristics, estSplitSize);
          this.prefix = null;
          return result;
        } else if (prefix != null) {
          Spliterator<T> result = prefix;
          this.prefix = null;
          return result;
        } else {
          return null;
        }
      }

      @Override
      public long estimateSize() {
        if (prefix != null) {
          estimatedSize = Math.max(estimatedSize, prefix.estimateSize());
        }
        return Math.max(estimatedSize, 0);
      }

      @Override
      public int characteristics() {
        return characteristics;
      }
    }
    return new FlatMapSpliterator(null, fromSpliterator, topCharacteristics, topSize);
  }
Returns a {@code Spliterator} that iterates over the elements of the spliterators generated by
applying {@code function} to the elements of {@code fromSpliterator}.