Java 通过谓词限制流
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20746429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Limit a stream by a predicate
提问by MForster
Is there a Java 8 stream operation that limits a (potentially infinite) Stream
until the first element fails to match a predicate?
是否有 Java 8 流操作限制(可能无限)Stream
直到第一个元素无法匹配谓词?
In Java 9 we can use takeWhile
as in the example below to print all the numbers less than 10.
在 Java 9 中,我们可以使用takeWhile
下面的示例来打印所有小于 10 的数字。
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
As there is no such operation in Java 8, what's the best way of implementing it in a general way?
由于 Java 8 中没有这样的操作,那么以一般方式实现它的最佳方法是什么?
采纳答案by Louis Wasserman
Such an operation ought to be possiblewith a Java 8 Stream
, but it can't necessarily be done efficiently -- for example, you can't necessarily parallelize such an operation, as you have to look at elements in order.
这样的操作应该是可能的与Java 8 Stream
,但它不一定能高效地完成-例如,你不一定能并行这样的操作,你一定要看看元素的顺序。
The API doesn't provide an easy way to do it, but what's probably the simplest way is to take Stream.iterator()
, wrap the Iterator
to have a "take-while" implementation, and then go back to a Spliterator
and then a Stream
. Or -- maybe -- wrap the Spliterator
, though it can't really be split anymore in this implementation.
API 没有提供一种简单的方法来做到这一点,但最简单的方法可能是 take Stream.iterator()
,将 包装Iterator
为具有“take-while”实现,然后返回到 aSpliterator
和 a Stream
。或者——也许——包装Spliterator
,尽管在这个实现中它不能再被拆分了。
Here's an untested implementation of takeWhile
on a Spliterator
:
这是一个未经测试的takeWhile
on a实现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);
}
回答by The Coordinator
Update: Java 9 Stream
now comes with a takeWhilemethod.
更新:Java 9Stream
现在带有takeWhile方法。
No needs for hacks or other solutions. Just use that!
不需要黑客或其他解决方案。就用那个!
I am sure this can be greatly improved upon: (someone could make it thread-safe maybe)
我相信这可以得到很大的改进:(也许有人可以使它成为线程安全的)
Stream<Integer> stream = Stream.iterate(0, n -> n + 1);
TakeWhile.stream(stream, n -> n < 10000)
.forEach(n -> System.out.print((n == 0 ? "" + n : "," + n)));
A hack for sure... Not elegant - but it works ~:D
肯定是一个黑客......不优雅 - 但它有效~:D
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);
}
}
回答by Chris Greenaway
Here is a version done on ints - as asked in the question.
这是一个在整数上完成的版本 - 如问题中所问。
Usage:
用法:
StreamUtil.takeWhile(IntStream.iterate(1, n -> n + 1), n -> n < 10);
Here's code for StreamUtil:
这是 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;
}
}
}
回答by Dominic Fox
takeWhile
is one of the functions provided by the protonpack library.
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));
回答by user2504380
I have another quick solution by implementing this (which is rly unclean in fact, but you get the idea):
我通过实现这个有另一个快速解决方案(实际上这是不干净的,但你明白了):
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();
}
};
}
}
回答by Michael Rowley
allMatch()
is a short-circuiting function, so you can use it to stop processing. The main disadvantage is that you have to do your test twice: once to see if you should process it, and again to see whether to keep going.
allMatch()
是一个短路功能,因此您可以使用它来停止处理。主要的缺点是你必须做两次测试:一次是看你是否应该处理它,另一次是看是否继续。
IntStream
.iterate(1, n -> n + 1)
.peek(n->{if (n<10) System.out.println(n);})
.allMatch(n->n < 10);
回答by frhack
You can use java8 + rxjava.
您可以使用 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));
回答by climbing_bum
Here is my attempt using just Java Stream library.
这是我仅使用 Java Stream 库的尝试。
IntStream.iterate(0, i -> i + 1)
.filter(n -> {
if (n < 10) {
System.out.println(n);
return false;
} else {
return true;
}
})
.findAny();
回答by Stuart Marks
Operations takeWhile
and dropWhile
have been added to JDK 9. Your example code
操作takeWhile
并dropWhile
已添加到 JDK 9。您的示例代码
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
will behave exactly as you expect it to when compiled and run under JDK 9.
在 JDK 9 下编译和运行时,它的行为将完全符合您的预期。
JDK 9 has been released. It is available for download here: http://jdk.java.net/9/
JDK 9 已经发布。可在此处下载:http: //jdk.java.net/9/
回答by Tagir Valeev
As a follow-up to @StuartMarks answer. My StreamExlibrary has the takeWhile
operation which is compatible with current JDK-9 implementation. When running under JDK-9 it will just delegate to the JDK implementation (via MethodHandle.invokeExact
which is really fast). When running under JDK-8, the "polyfill" implementation will be used. So using my library the problem can be solved like this:
作为@StuartMarks answer的后续行动。我的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);