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

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

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

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


当前回答

您可以使用java8 + rxjava。

import java.util.stream.IntStream;
import rx.Observable;


// Example 1)
IntStream intStream  = IntStream.iterate(1, n -> n + 1);
Observable.from(() -> intStream.iterator())
    .takeWhile(n ->
          {
                System.out.println(n);
                return n < 10;
          }
    ).subscribe() ;


// Example 2
IntStream intStream  = IntStream.iterate(1, n -> n + 1);
Observable.from(() -> intStream.iterator())
    .takeWhile(n -> n < 10)
    .forEach( n -> System.out.println(n));

其他回答

如果你有不同的问题,可能需要不同的解决方案,但对于你当前的问题,我只想说:

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

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

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

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));

去图书馆拿算盘——普通的算盘。它提供了你想要的API和更多:

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

声明:我是AbacusUtil的开发者。

可能有点离题了,但这是List<T>而不是Stream<T>。

首先你需要有一个take util方法。该方法接受前n个元素:

static <T> List<T> take(List<T> l, int n) {
    if (n <= 0) {
        return newArrayList();
    } else {
        int takeTo = Math.min(Math.max(n, 0), l.size());
        return l.subList(0, takeTo);
    }
}

它就像scala。list。take一样

    assertEquals(newArrayList(1, 2, 3), take(newArrayList(1, 2, 3, 4, 5), 3));
    assertEquals(newArrayList(1, 2, 3), take(newArrayList(1, 2, 3), 5));

    assertEquals(newArrayList(), take(newArrayList(1, 2, 3), -1));
    assertEquals(newArrayList(), take(newArrayList(1, 2, 3), 0));

现在,编写一个基于take的takeWhile方法就相当简单了

static <T> List<T> takeWhile(List<T> l, Predicate<T> p) {
    return l.stream().
            filter(p.negate()).findFirst(). // find first element when p is false
            map(l::indexOf).        // find the index of that element
            map(i -> take(l, i)).   // take up to the index
            orElse(l);  // return full list if p is true for all elements
}

它是这样工作的:

    assertEquals(newArrayList(1, 2, 3), takeWhile(newArrayList(1, 2, 3, 4, 3, 2, 1), i -> i < 4));

这个实现部分迭代列表几次,但它不会增加O(n^2)个操作。希望你能接受。