Java流`generate()`how to";包括「;第一个",;不包括;要素
假设Java流的这种使用场景,其中数据是从数据源添加的。数据源可以是值列表,如下面的示例所示,也可以是分页的RESTAPI。现在没关系Java流`generate()`how to";包括「;第一个",;不包括;要素,java,java-stream,Java,Java Stream,假设Java流的这种使用场景,其中数据是从数据源添加的。数据源可以是值列表,如下面的示例所示,也可以是分页的RESTAPI。现在没关系 import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; public class Main { public static void main(String[] args) { fin
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
final List<Boolean> dataSource = List.of(true, true, true, false, false, false, false);
final AtomicInteger index = new AtomicInteger();
Stream
.generate(() -> {
boolean value = dataSource.get(index.getAndIncrement());
System.out.format("--> Executed expensive operation to retrieve data: %b\n", value);
return value;
})
.takeWhile(value -> value == true)
.forEach(data -> System.out.printf("--> Using: %b\n", data));
}
}
正如您所看到的,最后一个元素(计算结果为false
)未按预期添加到流中
现在假设generate()
方法从RESTAPI加载数据页。在这种情况下,值true/false
是第N
页上的一个值,指示是否存在第N+1
页,类似于的内容有更多的字段。现在,我希望将API返回的最后一个页面添加到流中,但我不希望执行另一个昂贵的操作来读取空页面,因为我已经知道没有更多页面了
使用Java流API实现这一点最惯用的方法是什么?我能想到的每一种解决方法都需要执行对API的调用
更新
除了表中列出的方法外,还有另一种丑陋的方法可以实现这一点
public static void main(String[] args) {
final List<Boolean> dataSource = List.of(true, true, true, false, false, false, false);
final AtomicInteger index = new AtomicInteger();
final AtomicBoolean hasMore = new AtomicBoolean(true);
Stream
.generate(() -> {
if (!hasMore.get()) {
return null;
}
boolean value = dataSource.get(index.getAndIncrement());
hasMore.set(value);
System.out.format("--> Executed expensive operation to retrieve data: %b\n", value);
return value;
})
.takeWhile(Objects::nonNull)
.forEach(data -> System.out.printf("--> Using: %b\n", data));
}
publicstaticvoidmain(字符串[]args){
最终列表数据源=List.of(真、真、真、假、假、假、假);
最终AtomicInteger索引=新的AtomicInteger();
最终AtomicBoolean hasMore=新的AtomicBoolean(真);
流动
.生成(()->{
如果(!hasMore.get()){
返回null;
}
布尔值=dataSource.get(index.getAndIncrement());
hasMore.set(值);
System.out.format(“-->执行了昂贵的操作以检索数据:%b\n”,值);
返回值;
})
.takeWhile(对象::非空)
.forEach(数据->系统.out.printf(“-->使用:%b\n”,数据));
}
您在工作中使用了错误的工具。正如您的代码示例中已经注意到的那样,传递给的供应商
必须竭尽全力维护获取页面所需的索引
更糟糕的是:
返回无限顺序无序流,其中每个元素由提供的供应商生成。
这适用于生成恒定流、随机元素流等
您不会返回常量或随机值,也不会返回与顺序无关的任何其他值
这对以下方面具有重大影响:
否则,如果该流是无序的,则返回一个由从该流中提取的与给定谓词匹配的元素子集组成的流
如果你仔细想想,这是有道理的。如果至少有一个元素被谓词拒绝,它可能会在无序流的任意位置遇到,因此在此之前遇到的任意元素子集(包括空集)将是有效的前缀
但是,由于无序流没有“之前”或“之后”,因此,即使生成器在被拒绝流之后生成的元素也可以包含在结果中
实际上,对于顺序流,您不太可能遇到这样的效果,但这不会改变以下事实:stream.generate(…).takeWhile(…)
在语义上不适合您的任务
从您的示例代码中,我得出结论,页面不包含自己的编号,也不包含“getNext”方法,因此我们必须维护编号和“hasNext”状态以创建流
假设设置示例如下
class Page {
private String data;
private boolean hasNext;
public Page(String data, boolean hasNext) {
this.data = data;
this.hasNext = hasNext;
}
public String getData() {
return data;
}
public boolean hasNext() {
return hasNext;
}
}
请注意:
将给定的下一个函数迭代应用于初始元素时产生的顺序流,
条件是满足给定的hasNext谓词
当然,如果页面知道自己的编号,事情会简单得多,例如
Stream.iterate(getPage(0), Objects::nonNull,
p -> p.hasNext()? getPage(p.getPageNumber()+1): null)
.forEach(page -> System.out.println(page.getData()));
或者如果有一种方法可以从现有页面转到下一页面,例如
Stream.iterate(getPage(0), Objects::nonNull, p -> p.hasNext()? p.getNextPage(): null)
.forEach(page -> System.out.println(page.getData()));
听起来您想要构建一个迭代器,它有两个方法:hasNext
和next
。你能描述一下你是如何计算出hasNext的吗?简单地实现迭代器
接口更适合您的用例,您不需要真正的流
。1:true、2:true、3:true、4:false->1:true、2:true、3:true、4:true、5:false(true/false表示当前页面而不是下一个页面),然后使用takeWhile来获得您想要的内容。如果将此短语转换为类似的for循环方法,如果说您正在寻找类似boolean fetchNextPage=true的内容,是否正确;对于(int idx=0;idx)执行了昂贵的操作来检索数据:%b\n,value);if(!value){fetchNextPage=false;}System.out.printf(-->使用:%b\n,value)}
?谢谢大家!事实上,在@SamuelPhilipp发布答案之前,我没有找到答案。将此作为副本关闭。感谢您的澄清和扩展的答案。
Stream.iterate(Map.entry(0, getPage(0)), Objects::nonNull,
e -> e.getValue().hasNext()? Map.entry(e.getKey()+1, getPage(e.getKey()+1)): null)
.map(Map.Entry::getValue)
.forEach(page -> System.out.println(page.getData()));
Stream.iterate(getPage(0), Objects::nonNull,
p -> p.hasNext()? getPage(p.getPageNumber()+1): null)
.forEach(page -> System.out.println(page.getData()));
Stream.iterate(getPage(0), Objects::nonNull, p -> p.hasNext()? p.getNextPage(): null)
.forEach(page -> System.out.println(page.getData()));