是否有Java 8流操作限制流(可能是无限的),直到第一个元素无法匹配谓词?
在Java 9中,我们可以像下面的例子一样使用takeWhile来打印所有小于10的数字。
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
因为在Java 8中没有这样的操作,那么以通用的方式实现它的最佳方法是什么呢?
是否有Java 8流操作限制流(可能是无限的),直到第一个元素无法匹配谓词?
在Java 9中,我们可以像下面的例子一样使用takeWhile来打印所有小于10的数字。
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
因为在Java 8中没有这样的操作,那么以通用的方式实现它的最佳方法是什么呢?
当前回答
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));
其他回答
如果你有不同的问题,可能需要不同的解决方案,但对于你当前的问题,我只想说:
IntStream
.iterate(1, n -> n + 1)
.limit(10)
.forEach(System.out::println);
我有另一个快速的解决方案来实现这个(实际上是不干净的,但你知道的):
public static void main(String[] args) {
System.out.println(StreamUtil.iterate(1, o -> o + 1).terminateOn(15)
.map(o -> o.toString()).collect(Collectors.joining(", ")));
}
static interface TerminatedStream<T> {
Stream<T> terminateOn(T e);
}
static class StreamUtil {
static <T> TerminatedStream<T> iterate(T seed, UnaryOperator<T> op) {
return new TerminatedStream<T>() {
public Stream<T> terminateOn(T e) {
Builder<T> builder = Stream.<T> builder().add(seed);
T current = seed;
while (!current.equals(e)) {
current = op.apply(current);
builder.add(current);
}
return builder.build();
}
};
}
}
您可以使用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));
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));
可能有点离题了,但这是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)个操作。希望你能接受。