MFo*_*ter 179 java java-8 java-stream
是否有Java 8流操作限制(可能是无限的)Stream直到第一个元素无法匹配谓词?
在Java 9中,我们可以使用takeWhile下面的示例来打印小于10的所有数字.
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
由于Java 8中没有这样的操作,以一般方式实现它的最佳方法是什么?
Stu*_*rks 143
操作takeWhile并dropWhile已添加到JDK 9.您的示例代码
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
在JDK 9下编译和运行时,它的行为与您期望的完全相同.
JDK 9已经发布.它可以在这里下载:http://jdk.java.net/9/
Lou*_*man 80
使用Java 8 应该可以实现这样的操作Stream,但它不一定能够有效地完成 - 例如,您不一定能够并行化这样的操作,因为您必须按顺序查看元素.
API没有提供一种简单的方法,但最简单的方法是采取Stream.iterator(),包装Iterator有一个"需要时间"的实现,然后回到a Spliterator然后a Stream.或者 - 也许 - 包装Spliterator,虽然在这个实现中它不能再被拆分了.
下面是一个未经测试的执行takeWhile上Spliterator:
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);
}
Run Code Online (Sandbox Code Playgroud)
小智 49
allMatch()是一种短路功能,因此您可以使用它来停止处理.主要的缺点是你必须做两次测试:一次是看你是否应该处理它,再看看是否继续进行.
IntStream
.iterate(1, n -> n + 1)
.peek(n->{if (n<10) System.out.println(n);})
.allMatch(n->n < 10);
Run Code Online (Sandbox Code Playgroud)
Tag*_*eev 34
作为@StuartMarks的后续回答.我的StreamEx库具有takeWhile与当前JDK-9实现兼容的操作.在JDK-9下运行时,它将委托给JDK实现(通过MethodHandle.invokeExact它实现非常快).在JDK-8下运行时,将使用"polyfill"实现.所以使用我的库可以解决这个问题:
IntStreamEx.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
Dom*_*Fox 13
takeWhile是protonpack库提供的功能之一.
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));
Run Code Online (Sandbox Code Playgroud)
更新:Java 9 Stream现在带有一个takeWhile方法.
无需黑客或其他解决方案.只是用它!
我相信这可以在以下方面得到很大改善:(有人可能会让它成为线程安全的)
Stream<Integer> stream = Stream.iterate(0, n -> n + 1);
TakeWhile.stream(stream, n -> n < 10000)
.forEach(n -> System.out.print((n == 0 ? "" + n : "," + n)));
Run Code Online (Sandbox Code Playgroud)
class TakeWhile<T> implements Iterator<T> {
private final Iterator<T> iterator;
private final Predicate<T> predicate;
private volatile T next;
private volatile boolean keepGoing = true;
public TakeWhile(Stream<T> s, Predicate<T> p) {
this.iterator = s.iterator();
this.predicate = p;
}
@Override
public boolean hasNext() {
if (!keepGoing) {
return false;
}
if (next != null) {
return true;
}
if (iterator.hasNext()) {
next = iterator.next();
keepGoing = predicate.test(next);
if (!keepGoing) {
next = null;
}
}
return next != null;
}
@Override
public T next() {
if (next == null) {
if (!hasNext()) {
throw new NoSuchElementException("Sorry. Nothing for you.");
}
}
T temp = next;
next = null;
return temp;
}
public static <T> Stream<T> stream(Stream<T> s, Predicate<T> p) {
TakeWhile tw = new TakeWhile(s, p);
Spliterator split = Spliterators.spliterator(tw, Integer.MAX_VALUE, Spliterator.ORDERED);
return StreamSupport.stream(split, false);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用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));
Run Code Online (Sandbox Code Playgroud)
实际上,在Java 8中有两种方法可以实现此目的,而无需任何额外的库或使用Java 9。
如果要在控制台上打印2到20之间的数字,可以执行以下操作:
IntStream.iterate(2, (i) -> i + 2).peek(System.out::println).allMatch(i -> i < 20);
Run Code Online (Sandbox Code Playgroud)
要么
IntStream.iterate(2, (i) -> i + 2).peek(System.out::println).anyMatch(i -> i >= 20);
Run Code Online (Sandbox Code Playgroud)
在两种情况下,输出均为:
2
4
6
8
10
12
14
16
18
20
Run Code Online (Sandbox Code Playgroud)
尚无人提及任何比赛。这就是这篇文章的原因。
这是从JDK 9 java.util.stream.Stream.takeWhile(Predicate)复制的源代码.与JDK 8一起使用有点不同.
static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> p) {
class Taking extends Spliterators.AbstractSpliterator<T> implements Consumer<T> {
private static final int CANCEL_CHECK_COUNT = 63;
private final Spliterator<T> s;
private int count;
private T t;
private final AtomicBoolean cancel = new AtomicBoolean();
private boolean takeOrDrop = true;
Taking(Spliterator<T> s) {
super(s.estimateSize(), s.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED));
this.s = s;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
boolean test = true;
if (takeOrDrop && // If can take
(count != 0 || !cancel.get()) && // and if not cancelled
s.tryAdvance(this) && // and if advanced one element
(test = p.test(t))) { // and test on element passes
action.accept(t); // then accept element
return true;
} else {
// Taking is finished
takeOrDrop = false;
// Cancel all further traversal and splitting operations
// only if test of element failed (short-circuited)
if (!test)
cancel.set(true);
return false;
}
}
@Override
public Comparator<? super T> getComparator() {
return s.getComparator();
}
@Override
public void accept(T t) {
count = (count + 1) & CANCEL_CHECK_COUNT;
this.t = t;
}
@Override
public Spliterator<T> trySplit() {
return null;
}
}
return StreamSupport.stream(new Taking(stream.spliterator()), stream.isParallel()).onClose(stream::close);
}
Run Code Online (Sandbox Code Playgroud)