目的
- 自力で任意のストリームを作る最善の方法…はわかりませんが、とりあえず 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);
});
無限ストリームになります。