Поток Java 8 IllegalStateException

Я пытаюсь вычислить простые числа с помощью потоков Java8, но получаю исключение IllegalStateException: поток уже обработан или закрыт.

Это мой код:

package experimentations.chapter02;

import java.util.stream.Stream;

public class PrimesStream {
    public static void main(String[] args) {
        Stream.iterate(0, e-> e+1).filter(PrimesStream::isPrime).limit(10).forEach(System.out::println);
    }

    public static boolean isPrime(int i){
        if (i < 2) return false;
        Stream<Integer> stream =  Stream.iterate(2, e -> e+1);
        stream.limit(i - 2);
        return !stream.anyMatch(divisor -> i%divisor == 0);
    }

}

А это трассировка стека:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
    at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:449)
    at experimentations.chapter02.PrimesStream.isPrime(PrimesStream.java:14)
    at experimentations.chapter02.PrimesStream$$Lambda$2/918221580.test(Unknown Source)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1812)
    at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
    at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:529)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:516)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at experimentations.chapter02.PrimesStream.main(PrimesStream.java:7)

У меня такое ощущение, что это потому, что я пытаюсь применить вызов потока к другому, но даже с этой мыслью я не знаю, что является самым простым обходным решением.


person loloof64    schedule 07.04.2014    source источник
comment
Также обратите внимание, что я должен был ограничить генератор числами i-2 (отредактировано): иначе я не получу никакого простого числа.   -  person loloof64    schedule 07.04.2014


Ответы (2)


Вам нужно связать свои потоковые методы, это исправит это, поэтому ваш isPrime должен выглядеть так:

return !Stream.iterate(2, e -> e + 1)
    .limit(i - 1)
    .anyMatch(divisor -> i % divisor == 0);
person skiwi    schedule 07.04.2014
comment
На самом деле я только что заметил, что можно обойтись и без цепочки: просто сохраняя каждую операцию в разных потоках‹Integer›. Но ИМХО цепочка лучше в большинстве случаев. - person loloof64; 07.04.2014
comment
Я думаю, это неправильно. Насколько я знаю, никогда не нужно связывать это. Вам просто нужно убедиться, что вы вызываете только один terminal operation в потоке. - person F. Böller; 19.08.2014
comment
@F.Böller Если вы не свяжете, вам нужно сохранить полученное IntStream в себе, если вы его не сохраните, все будет потеряно. - person skiwi; 19.08.2014
comment
Хорошо, я проверил это, и вы правы. Но я до сих пор не понимаю, почему. У меня никогда не было проблем с сохранением потока в локальной переменной. - person F. Böller; 19.08.2014
comment
@ F.Böller Это потому, что любой экземпляр потока неизменяем, поэтому выполнение любых других операций ничего не изменит в этом потоке. Однако он возвращает новое состояние, поэтому он работает, когда вы сохраняете его в переменной. - person skiwi; 19.08.2014

Вот пример кода, который вызывает это исключение.

List<Person> people = Arrays.asList(
 new Person("Bob", 17),
 new Person("Dave", 23),
 new Person("Joe", 32));

Stream<String> nameStream = people.stream()
 .filter(person -> person.getAge() > 21)
 .map(Person::getName);

nameStream.forEach(System.out::println);

// When stream values are gone, they are gone. Let’s try again: 
nameStream.forEach(System.out::println); 

java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)

Таким образом, вы можете выполнять только одну операцию на максимуме в потоках.

Проверьте здесь, чтобы узнать больше о функциях java8.

person pgollangi    schedule 19.08.2014