通过谓词限制流

2022-08-31 06:05:05

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

在Java 9中,我们可以使用如下例所示来打印所有小于10的数字。takeWhile

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

由于Java 8中没有这样的操作,那么以一般方式实现它的最佳方法是什么?


答案 1

操作并已添加到 JDK 9 中。您的示例代码takeWhiledropWhile

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

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

JDK 9 已经发布。可在此处下载:JDK 9 版本


答案 2

这样的操作应该可以在 Java 8 中实现,但不一定能够有效地完成 - 例如,您不一定可以并行化这样的操作,因为您必须按顺序查看元素。Stream

API没有提供一种简单的方法来做到这一点,但最简单的方法可能是,包装迭代器以获得“take-while”实现,然后返回到a,然后是a。或者 - 也许 - 包装拆分器,尽管它在此实现中无法再真正拆分。Stream.iterator()SpliteratorStream

下面是在 上未经测试的实现:takeWhileSpliterator

static <T> Spliterator<T> takeWhile(
    Spliterator<T> splitr, Predicate<? super T> predicate) {
  return new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) {
    boolean stillGoing = true;
    @Override public boolean tryAdvance(Consumer<? super T> consumer) {
      if (stillGoing) {
        boolean hadNext = splitr.tryAdvance(elem -> {
          if (predicate.test(elem)) {
            consumer.accept(elem);
          } else {
            stillGoing = false;
          }
        });
        return hadNext && stillGoing;
      }
      return false;
    }
  };
}

static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> predicate) {
   return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false);
}

推荐