Java8で独自のStreamを作成する

配列やコレクションを使わずにInputStream等からStreamを作成する方法。

## [Stream.of(T... values)](http://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html#of-T...-)

- 可変長引数で要素を指定してStreamを作成する。
- テストデータやサンプル作成でよく使う。

```java:
Stream<String> stream = Stream.of("a", "b");
stream.forEach(System.out::println);
```

## [Stream.builder()](http://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html#builder--)

- ビルダーでStreamを作成する。

```java:
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<T> s)](http://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html#generate-java.util.function.Supplier-)

- 順序付けされていない無限ストリームを作成する。
- 乱数の生成など、次の値が前の値と関連性がない場合に使用する。

```java:
Stream<Double> stream = Stream.generate(() -> Math.random());
stream.limit(3).forEach(System.out::println);
```

※上記はDoubleStreamでも可能。
※乱数の生成するStreamは[Randomクラス](http://docs.oracle.com/javase/jp/8/docs/api/java/util/Random.html)のメソッドでも提供されている。

## [Stream.iterate(T seed, UnaryOperator<T> f)](http://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html#iterate-T-java.util.function.UnaryOperator-)

- 順序付けされた無限ストリームを作成する。
- 引数には初期要素seedと２番目以降の要素を生成する関数fを指定する。
- 関数fには前の要素が渡されるので、前の値を加工して次の値を生成する場合に使用する。

```java:
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<T> spliterator, boolean parallel)](http://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/StreamSupport.html#stream-java.util.Spliterator-boolean-)

- [Spliterator](http://docs.oracle.com/javase/jp/8/docs/api/java/util/Spliterator.html)からStreamを作成する。
- Spliteratorは分割可能なIteratorのことで、[Spliterators](http://docs.oracle.com/javase/jp/8/docs/api/java/util/Spliterators.html)のstaticメソッド`spliterator()`や`spliteratorUnknownSize()`を使用してIteratorから作成する。
- InputStreamから読み込んだデータをStreamで返すような場合に使用する。

### 乱数を生成するStreamの例

以下は乱数を生成するIteratorを作成し、そこからSpliterator,Streamを作成する例。

```java:
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](https://hc.apache.org/httpcomponents-client-ga/)と[google-gson](https://code.google.com/p/google-gson/)を使用。

```java: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件ずつ読み込んで処理できるので、大量のデータを処理する場合でも対応できるようになる。