配列やコレクションを使わずにInputStream等からStreamを作成する方法。
Stream.of(T... values)
- 可変長引数で要素を指定してStreamを作成する。
- テストデータやサンプル作成でよく使う。
Stream<String> stream = Stream.of("a", "b");
stream.forEach(System.out::println);
Stream.builder()
- ビルダーでStreamを作成する。
Stream.Builder<Integer> builder = Stream.builder();
for (int i = 0; i < 5; i++) {
builder.add(i);
}
Stream<Integer> stream = builder.build();
stream.forEach(System.out::println);
※上記はIntStreamでも可能。
Stream.generate(Supplier s)
- 順序付けされていない無限ストリームを作成する。
- 乱数の生成など、次の値が前の値と関連性がない場合に使用する。
Stream<Double> stream = Stream.generate(() -> Math.random());
stream.limit(3).forEach(System.out::println);
※上記はDoubleStreamでも可能。
※乱数の生成するStreamはRandomクラスのメソッドでも提供されている。
Stream.iterate(T seed, UnaryOperator f)
- 順序付けされた無限ストリームを作成する。
- 引数には初期要素seedと2番目以降の要素を生成する関数fを指定する。
- 関数fには前の要素が渡されるので、前の値を加工して次の値を生成する場合に使用する。
Stream<Integer> stream = Stream.iterate(2, x -> x * 2);
stream.limit(8).forEach(System.out::println);
//結果
//2
//4
//8
//16
//32
//64
//128
//256
StreamSupport.stream(Spliterator spliterator, boolean parallel)
- SpliteratorからStreamを作成する。
- Spliteratorは分割可能なIteratorのことで、Spliteratorsのstaticメソッド
spliterator()
やspliteratorUnknownSize()
を使用してIteratorから作成する。 - InputStreamから読み込んだデータをStreamで返すような場合に使用する。
乱数を生成するStreamの例
以下は乱数を生成するIteratorを作成し、そこからSpliterator,Streamを作成する例。
Iterator<Integer> itr = new Iterator<Integer>() {
public boolean hasNext() {
return true;
}
public Integer next() {
return (int) (Math.random() * 100);
}
};
Spliterator<Integer> spliterator =
Spliterators.spliteratorUnknownSize(itr, Spliterator.NONNULL);
Stream<Integer> stream = StreamSupport.stream(spliterator, true);
stream.limit(5).forEach(System.out::println);
- 先ず乱数を生成するIteratorを作成する。(itr)
- 次にSpliterators.spliteratorUnknownSize()でSpliteratorを作成する。(spliterator)
- 第1引数にはIterator、第2引数にはSpliteratorの特性をSpliteratorの定数で指定する。
- 最後にStreamSupport.stream()でStreamを作成する。
QiitaAPIで投稿一覧を取得してStreamで返す例
もう少し実践的な例として、QiitaAPIで投稿一覧を取得してStreamで返す例。
Apache HttpClientとgoogle-gsonを使用。
QiitaApiExamples.java
import static java.util.Spliterator.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
public class QiitaApiExamples {
public static void items(Consumer<Stream<Item>> consumer)
throws ClientProtocolException, IOException {
Gson gson = new Gson();
HttpGet httpGet = new HttpGet("http://qiita.com/api/v2/items");
try (
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse res = httpClient.execute(httpGet);
JsonReader reader = new JsonReader(new InputStreamReader(
res.getEntity().getContent(), StandardCharsets.UTF_8))
) {
reader.beginArray();
Iterator<Item> itr = new Iterator<Item>() {
public boolean hasNext() {
try {
return reader.hasNext();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public Item next() {
return gson.fromJson(reader, Item.class);
}
};
Spliterator<Item> spliterator = Spliterators.spliteratorUnknownSize(
itr, NONNULL | ORDERED | SIZED);
Stream<Item> stream = StreamSupport.stream(spliterator, false);
consumer.accept(stream);
reader.endArray();
}
}
public static void main(String[] args) throws Exception {
items(stream -> stream.forEach(System.out::println));
}
}
レスポンスからオブジェクトを生成する際には、JsonReaderを使用して投稿を1件ずつオブジェクトに変換する。
通常はItem[] items = gson.fromJson(reader, Item[].class)
とやれば一度に全データを取得できるが、この場合全データをメモリに展開することになるのでメモリを圧迫する。
上記のようにStream(Iterator)を使用すれば1件ずつ読み込んで処理できるので、大量のデータを処理する場合でも対応できるようになる。