是否有Java 8流操作限制流(可能是无限的),直到第一个元素无法匹配谓词?

在Java 9中,我们可以像下面的例子一样使用takeWhile来打印所有小于10的数字。

IntStream
    .iterate(1, n -> n + 1)
    .takeWhile(n -> n < 10)
    .forEach(System.out::println);

因为在Java 8中没有这样的操作,那么以通用的方式实现它的最佳方法是什么呢?


当前回答

allMatch()是一个短路函数,因此可以使用它来停止处理。主要的缺点是您必须进行两次测试:一次是查看是否应该处理它,另一次是查看是否继续进行。

IntStream
    .iterate(1, n -> n + 1)
    .peek(n->{if (n<10) System.out.println(n);})
    .allMatch(n->n < 10);

其他回答

takeWhile是质子包库提供的函数之一。

Stream<Integer> infiniteInts = Stream.iterate(0, i -> i + 1);
Stream<Integer> finiteInts = StreamUtils.takeWhile(infiniteInts, i -> i < 10);

assertThat(finiteInts.collect(Collectors.toList()),
           hasSize(10));

allMatch()是一个短路函数,因此可以使用它来停止处理。主要的缺点是您必须进行两次测试:一次是查看是否应该处理它,另一次是查看是否继续进行。

IntStream
    .iterate(1, n -> n + 1)
    .peek(n->{if (n<10) System.out.println(n);})
    .allMatch(n->n < 10);

操作takeWhile和dropWhile已添加到JDK 9中。示例代码

IntStream
    .iterate(1, n -> n + 1)
    .takeWhile(n -> n < 10)
    .forEach(System.out::println);

在JDK 9下编译和运行时,它的行为将完全符合您的预期。

JDK 9已经发布。可以在这里下载:JDK 9发行版。

如果您知道将要执行的重复的确切数量,您就可以执行

IntStream
          .iterate(1, n -> n + 1)
          .limit(10)
          .forEach(System.out::println);

这是在int上做的一个版本-正如问题中所问的那样。

用法:

StreamUtil.takeWhile(IntStream.iterate(1, n -> n + 1), n -> n < 10);

下面是StreamUtil的代码:

import java.util.PrimitiveIterator;
import java.util.Spliterators;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;

public class StreamUtil
{
    public static IntStream takeWhile(IntStream stream, IntPredicate predicate)
    {
        return StreamSupport.intStream(new PredicateIntSpliterator(stream, predicate), false);
    }

    private static class PredicateIntSpliterator extends Spliterators.AbstractIntSpliterator
    {
        private final PrimitiveIterator.OfInt iterator;
        private final IntPredicate predicate;

        public PredicateIntSpliterator(IntStream stream, IntPredicate predicate)
        {
            super(Long.MAX_VALUE, IMMUTABLE);
            this.iterator = stream.iterator();
            this.predicate = predicate;
        }

        @Override
        public boolean tryAdvance(IntConsumer action)
        {
            if (iterator.hasNext()) {
                int value = iterator.nextInt();
                if (predicate.test(value)) {
                    action.accept(value);
                    return true;
                }
            }

            return false;
        }
    }
}