LoginSignup
35
38

More than 5 years have passed since last update.

Java8で独自のStreamを作成する

Posted at

配列やコレクションを使わずに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 HttpClientgoogle-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件ずつ読み込んで処理できるので、大量のデータを処理する場合でも対応できるようになる。

35
38
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
35
38