web-dev-qa-db-ja.com

ストリームを再利用する方法はありますか?

私は新しいJava 8機能を学んでいますが、ストリーム(Java.util.stream.Stream)とコレクターを試しながら、ストリームを2回使用できないことに気付きました。

再利用する方法はありますか?

53
Francisco

ストリームを再利用したい場合は、ストリーム式をサプライヤでラップし、新しい式が必要なときはいつでもmyStreamSupplier.get()を呼び出すことができます。例えば:

Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());
63
Hank D

ドキュメント から:

ストリームの操作(中間または端末ストリーム操作の呼び出し)は1回のみです。

ストリームが再利用されていることを検出した場合、ストリーム実装はIllegalStateExceptionをスローします。

したがって、答えは「いいえ」です。ストリームは再利用することを意図していません。

59
Vince Emigh

他の人が言ったように、「できません」。

しかし、多くの基本的な操作には便利なsummaryStatistics()を覚えておくと便利です。

代わりに:

List<Person> personList = getPersons();

personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();

あなたはできる:

// Can also be DoubleSummaryStatistics from mapToDouble()

IntSummaryStatistics stats = personList.stream()
                                       .mapToInt(p-> p.getAge())
                                       .summaryStatistics();

stats.getAverage();
stats.getMin();
stats.getMax();
23
Andrejs

ストリームの全体的な考え方は、一度限りだということです。これにより、中間ストレージなしで、再入力不可のソースを作成できます(たとえば、ネットワーク接続から行を読み取る)。ただし、Streamコンテンツを再利用する場合は、中間コレクションにダンプして「ハードコピー」を取得できます。

Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list

ストリームをマテリアライズしたくない場合、場合によっては、同じストリームで一度にいくつかのことを行う方法があります。たとえば、詳細については this または this の質問を参照できます。

7
Tagir Valeev

考えてみると、このストリームの「再利用」の意志は、ニースのインライン操作で目的の結果を実行する意志にすぎません。それで、基本的に、ここで話していることは、端末操作を書いた後に処理を続けるために何ができるのでしょうか?

1)端末操作がcollectionを返す場合、すべてのコレクションをストリームに戻すことができるため、問題はすぐに解決されます(JDK 8)。

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .collect(Collectors.toList())
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

2)ターミナル操作がoptionalを返す場合、JDK 9のOptionalクラスの拡張により、Optional結果をストリームに変換し、目的のNiceインライン操作を取得できます。

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .findAny()
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

3)端末操作がその他を返す場合、そのような結果を処理するためにストリームを考慮する必要があることを本当に疑います:

List<Integer> l=Arrays.asList(5,10,14);
        boolean allEven=l.stream()
            .filter(nth-> nth>5)
            .allMatch(nth-> nth%2==0);
        if(allEven){
            ...
        }
1
BabaNew

他の人が指摘したように、ストリームオブジェクト自体は再利用できません。

しかし、ストリームを再利用する効果を得る1つの方法は、ストリーム作成コードを関数に抽出するです。

これを行うには、ストリーム作成コードを含むメソッドまたは関数オブジェクトを作成します。その後、複数回使用できます。

例:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

    // The normal way to use a stream:
    List<String> result1 = list.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    // The stream operation can be extracted to a local function to
    // be reused on multiple sources:
    Function<List<Integer>, List<String>> listOperation = l -> l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    List<String> result2 = listOperation.apply(list);
    List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));

    // Or the stream operation can be extracted to a static method,
    // if it doesn't refer to any local variables:
    List<String> result4 = streamMethod(list);

    // The stream operation can also have Stream as argument and return value,
    // so that it can be used as a component of a longer stream pipeline:
    Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i);

    List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
        .filter(s -> s.length() < 7)
        .sorted()
        .collect(toCollection(LinkedList::new));
}

public static List<String> streamMethod(List<Integer> l) {
    return l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());
}

一方、複数回繰り返し処理するストリームオブジェクトが既にある場合は、ストリームのコンテンツをコレクションオブジェクトに保存する必要があります。

その後、コレクションよりも同じコンテンツの複数のストリームを取得できます。

例:

public void test(Stream<Integer> stream) {
    // Create a copy of the stream elements
    List<Integer> streamCopy = stream.collect(toList());

    // Use the copy to get multiple streams
    List<Integer> result1 = streamCopy.stream() ...
    List<Integer> result2 = streamCopy.stream() ...
}
1
Lii

Functional Java library は、ユーザーが求めていることを実行する独自のストリームを提供します。その変換メソッドを使用して、Java SDKオブジェクトとFJオブジェクト間で変換できます。 Java8.JavaStream_Stream(stream)は、JDK 8ストリームを指定すると、再利用可能なFJストリームを返します。

0
Bill Burdick