是否有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中没有这样的操作,那么以通用的方式实现它的最佳方法是什么呢?
当前回答
甚至我也有类似的需求——调用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;
}
其他回答
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));
甚至我也有类似的需求——调用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;
}
可能有点离题了,但这是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)个操作。希望你能接受。
我有另一个快速的解决方案来实现这个(实际上是不干净的,但你知道的):
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();
}
};
}
}
如果您知道将要执行的重复的确切数量,您就可以执行
IntStream
.iterate(1, n -> n + 1)
.limit(10)
.forEach(System.out::println);