是否有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);
甚至我也有类似的需求——调用web服务,如果失败,重试3次。如果在多次尝试后仍然失败,请发送电子邮件通知。在谷歌搜索了很多之后,anyMatch()成为了救星。我的示例代码如下。在下面的例子中,如果webServiceCall方法在第一次迭代本身中返回true,则stream不会在我们调用anyMatch()时进一步迭代。我相信,这就是你想要的。
import java.util.stream.IntStream;
import io.netty.util.internal.ThreadLocalRandom;
class TrialStreamMatch {
public static void main(String[] args) {
if(!IntStream.range(1,3).anyMatch(integ -> webServiceCall(integ))){
//Code for sending email notifications
}
}
public static boolean webServiceCall(int i){
//For time being, I have written a code for generating boolean randomly
//This whole piece needs to be replaced by actual web-service client code
boolean bool = ThreadLocalRandom.current().nextBoolean();
System.out.println("Iteration index :: "+i+" bool :: "+bool);
//Return success status -- true or false
return bool;
}
IntStream.iterate(1, n -> n + 1)
.peek(System.out::println) //it will be executed 9 times
.filter(n->n>=9)
.findAny();
您可以使用mapToObj来返回最终对象或消息,而不是peak
IntStream.iterate(1, n -> n + 1)
.mapToObj(n->{ //it will be executed 9 times
if(n<9)
return "";
return "Loop repeats " + n + " times";});
.filter(message->!message.isEmpty())
.findAny()
.ifPresent(System.out::println);
allMatch()是一个短路函数,因此可以使用它来停止处理。主要的缺点是您必须进行两次测试:一次是查看是否应该处理它,另一次是查看是否继续进行。
IntStream
.iterate(1, n -> n + 1)
.peek(n->{if (n<10) System.out.println(n);})
.allMatch(n->n < 10);
这样的操作在Java 8 Stream中应该是可能的,但它不一定能高效地完成——例如,您不一定能将这样的操作并行化,因为您必须按顺序查看元素。
API并没有提供简单的方法,但最简单的方法可能是获取Stream. Iterator(),将迭代器包装为“take-while”实现,然后返回到Spliterator,然后是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);
}