LoginSignup
23
20

More than 5 years have passed since last update.

[Java] IteratorをStreamにする

Last updated at Posted at 2015-11-03

目的

  • 自力で任意のストリームを作る最善の方法…はわかりませんが、とりあえず Iterator があれば Stream にできます
  • すぐ忘れるのでメモ

サンプル

import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

//...

// Iterator を Stream にする
Spliterator<String> spliterator = Spliterators.spliteratorUnknownSize(iterator, 0);
Stream<String> stream = StreamSupport.stream(spliterator, false);

Spliterators.spliteratorUnknownSize() の第2引数に渡しているものは characteristics というデータの特性です。以下の値の論理和を指定します。

  • ORDERED - 順番が決まっている(例: List, LinkedHashSet, TreeSet)
  • DISTINCT - 重複なし(例: Set)
  • SORTED - ソートされている(例: SortedSet)
  • SIZED - サイズが決まっている(例: Collection)
  • NONNULL - null が含まれていない
  • IMMUTABLE - 変更不可能
  • CONCURRENT - マルチスレッドで更新可能
  • SUBSIZED - Spliteratorを分割した後でも、分割後Spliteratorのサイズが決まっている(暗黙的にSIZEDも指定される)

生成するデータの特性に応じて設定すると良いかと思います。

もし、サイズが決まっている場合(SIZED and/or SUBSIZED)には、 Spliterators.spliteratorUnknownSize() ではなく、 Spliterators.spliterator() を使う必要があります。

Spliterator<String> spliterator = Spliterators.spliterator(iterator, size, Spliterator.SIZED);

また、 StreamSupport.stream() の第2引数はパラレルストリームかどうかの指定です。

実装例

あまり賢くないロジックかもしれないけど。

/**
 * 有限ストリームを生成
 *
 * @param generator
 *            データを生成するサプライヤー. null を返した場合に終端と判断
 * @return ストリーム
 */
public static <T> Stream<T> stream(Supplier<T> generator) {
    Iterator<T> iterator = new SupplierIterator<T>(generator);
    Spliterator<T> spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL);
    Stream<T> stream = StreamSupport.stream(spliterator, false);
    return stream;
}

/**
 * Supplier を Iterator 化.<br>
 * Supplier が null を返したら終端だと判断します。
 *
 * @param <T>
 *            要素の型
 */
public static class SupplierIterator<T> implements Iterator<T> {
    /** データを生成するサプライヤー */
    private final Supplier<T> generator;
    /** 終端まで処理済みの場合に真 */
    private boolean isEof = false;
    /** 先読みした値. 先読みしていなければnull */
    private T nextValue;

    /**
     * コンストラクタ
     *
     * @param generator サプライヤー
     * @throws NullPointerException 引数が null の場合
     */
    public SupplierIterator(Supplier<T> generator) {
        this.generator = Objects.requireNonNull(generator);
    }

    @Override
    public boolean hasNext() {
        if (!isEof && nextValue == null) {
            // EOF でなく、先読みしていない場合
            nextValue = generator.get();
            // 読んだデータが null なら終端と判断
            isEof = (nextValue == null);
        }
        return !isEof;
    }

    @Override
    public T next() {
        if (hasNext()) {
            T value = nextValue;
            nextValue = null;
            return value;
        }
        throw new NoSuchElementException();
    }
}

それ以外のストリームの作り方

がんばってイテレータを作らなくても、他の方法でストリームを作ることもできます。

Stream#of(), Collection#stream(), Arrays#stream()

Stream<String> stream0 = Stream.of("A", "B", "C");

List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream1 = list.stream();

String[] array = { "A", "B", "C" };
Stream<String> stream2 = Arrays.stream(array);

Stream#builder()

Stream.Builder<String> builder = Stream.builder();
builder.accept("A");
builder.accept("B");
builder.accept("C");
Stream<String> stream3 = builder.build();

Stream<String> stream4 = Stream.<String> builder()
        .add("A")
        .add("B")
        .add("C")
        .build();

Stream#generate()

Supplier<String> generator = () -> method();
Stream<String> stream5 = Stream.generate(generator).limit(5);

無限ストリームになります。

Stream#iterate()

Stream<String> stream6 = Stream.iterate("S", (s) -> s + s);
stream6.limit(10).forEach(s -> {
    System.out.println(s);
});

無限ストリームになります。

23
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
20