Java 结束一个“问题”;“无限”;满足某些条件时的流
我正试图从一个REST风格的web服务中提取数据,该服务以页面形式提供内容 我能知道我已经到达终点的唯一方法是当我要求一页,但没有结果时。我想在那个时候终止流 我已经编写了以下Java代码。第一个函数从web服务中提取一个页面,并将其作为流返回。第二个函数将流平面映射到一个流中Java 结束一个“问题”;“无限”;满足某些条件时的流,java,java-stream,Java,Java Stream,我正试图从一个REST风格的web服务中提取数据,该服务以页面形式提供内容 我能知道我已经到达终点的唯一方法是当我要求一页,但没有结果时。我想在那个时候终止流 我已经编写了以下Java代码。第一个函数从web服务中提取一个页面,并将其作为流返回。第二个函数将流平面映射到一个流中 public Stream<ApplicationResponse> getApplications(String token, RestTemplate rt, Integer page, Integer
public Stream<ApplicationResponse> getApplications(String token, RestTemplate rt, Integer page, Integer pageSize) {
HttpEntity<String> entity = new HttpEntity<>("parameters", getHeaders(token));
String url = String.format("%s?PageIndex=%s&PageSize=%s", endpoint, page, pageSize);
ResponseEntity<ApplicationCollection> ar = rt.exchange(url, HttpMethod.GET, entity, ApplicationCollection.class);
ApplicationResponse[] res = Objects.requireNonNull(ar.getBody()).getData();
// Do something here when res is empty, so that the stream ends
return Arrays.stream(res);
}
public Stream<ApplicationResponse> getApplications(String token, RestTemplate rt) {
// This function does the right thing, exept when we run out of data!
return IntStream.iterate(1, i -> i + 1).mapToObj(i -> getApplications(token, rt, i, 500)).flatMap(Function.identity());
}
公共流getApplications(字符串标记、RestTemplate rt、整数页、整数页大小){
HttpEntity=新的HttpEntity(“参数”,getHeader(令牌));
字符串url=String.format(“%s?PageIndex=%s&PageSize=%s”,端点,页面,页面大小);
ResponseEntity ar=rt.exchange(url、HttpMethod.GET、entity、ApplicationCollection.class);
ApplicationResponse[]res=Objects.requirennull(ar.getBody()).getData();
//当res为空时,在此处执行某些操作,以便流结束
返回数组.stream(res);
}
公共流getApplications(字符串令牌、RestTemplate rt){
//这个函数做了正确的事情,当我们用完数据时就可以例外了!
返回IntStream.iterate(1,i->i+1).mapToObj(i->getApplications(token,rt,i,500)).flatMap(Function.identity());
}
问题是,我如何让这一切结束
如果我是用Python写这篇文章的,我会在我知道流中没有什么东西可以放的时候引发StopIteration异常。我能做些类似的事情吗
我能想到的最好的方法是使用null,或者引发一个异常来表示数据的结束,然后将流包装到一个迭代器中,该迭代器知道在接收到信号时停止。但是我还能做什么更惯用的事情吗?在霍尔格发表评论之后,我尝试了一下,并尝试使用
拆分器
而不是迭代器
。它确实更简单,因为next
和hasNext
是。。。有点像是组合成了tryAdvance
?它甚至足够短,只需将其内联到util方法imo中即可
public static Stream<ApplicationResponse> getApplications(String token, RestTemplate rt)
{
return StreamSupport.stream(new AbstractSpliterator<ApplicationResponse[]>(Long.MAX_VALUE,
Spliterator.ORDERED
| Spliterator.IMMUTABLE)
{
private int page = 1;
@Override
public boolean tryAdvance(Consumer<? super ApplicationResponse[]> action)
{
HttpEntity<String> entity = new HttpEntity<>("parameters", getHeaders(token));
String url = String.format("%s?PageIndex=%s&PageSize=%s", endpoint, page, 500);
ResponseEntity<ApplicationCollection> ar = rt.exchange(url, HttpMethod.GET, entity,
ApplicationCollection.class);
ApplicationResponse[] res = Objects.requireNonNull(ar.getBody()).getData();
if (res.length == 0)
return false;
page++;
action.accept(res);
return true;
}
}, false).flatMap(Arrays::stream);
}
publicstaticstreamgetapplications(字符串令牌,rest模板rt)
{
返回StreamSupport.stream(新的AbstractSpliterator(Long.MAX_值,
分路器
|拆分器(不可变)
{
私有int页=1;
@凌驾
公共布尔型优先权(Consumer…如果内部函数引发异常,有没有一种方法可以正常处理该异常并允许流结束?不是在您希望从操作中获得结果时。尝试使用Spliterator
而不是Iterator
。您会发现它实际上更简单。@Holger这种简单性即将到来事实上,这是一种“做”的单一方法,tryAdvance
?回来感觉很好,顺便说一句:)@Eugene是一个简单的方法,具有与任务匹配的直接逻辑。相反,两个迭代器
方法需要额外检查是否已经存在查询值。此外,在创建流
时,迭代器
必须包装到拆分器中。此答案的解决方案使用了一个附加值通过创建Iterable
调用spliterator()的nal迂回
这将在拆分器处结束。Spliterator Unknownsize
您也可以直接调用它,但在一开始实现拆分器时它不是必需的。@Holger当我开始尝试直接实现拆分器时,它在tryAdvance
周围相当大。我找到了抽象实现成功地缩短了它。记得重写forEachRemaining
,但发现在这种情况下它实际上没有提供任何好处。请执行所选参数(特征
,估计大小
)有意义。我是否正确理解了trySplit
的实现,因为Long。MAX_VALUE
确保它不会并行化?我只是注意到了这个注释,不过迟回答总比不回答好。位必须与二进制或,即拆分器组合。有序的|拆分器。不可变的
。不提供大小的de>和Long.MAX_值作为估计值具有“未知大小”的含义,但这并不意味着没有拆分支持。继承的实现将调用您的tryAdvance
,将元素缓冲到一个数组中,并返回一个基于数组的拆分器。这没有什么错,您的代码仍然只由一个线程调用。这只是意味着,如果您海因的行动很繁重。
public class ResponseIterator
implements Iterator<Stream<ApplicationResponse>>
{
private int page = 1;
private String token;
private RestTemplate rt;
private ApplicationResponse[] next;
private ResponseIterator(String token, RestTemplate rt)
{
this.token = token;
this.rt = rt;
}
public static Stream<ApplicationResponse> getApplications(String token, RestTemplate rt)
{
Iterable<Stream<ApplicationResponse>> iterable = () -> new ResponseIterator(token, rt);
return StreamSupport.stream(iterable.spliterator(), false).flatMap(Function.identity());
}
@Override
public boolean hasNext()
{
if (next == null)
{
next = getNext();
}
return next.length != 0;
}
@Override
public Stream<ApplicationResponse> next()
{
if (next == null)
{
next = getNext();
}
Stream<ApplicationResponse> nextStream = Arrays.stream(next);
next = getNext();
return nextStream;
}
private ApplicationResponse[] getNext()
{
HttpEntity<String> entity = new HttpEntity<>("parameters", getHeaders(token));
String url = String.format("%s?PageIndex=%s&PageSize=%s", endpoint, page, 500);
ResponseEntity<ApplicationCollection> ar = rt.exchange(url, HttpMethod.GET, entity,
ApplicationCollection.class);
ApplicationResponse[] res = Objects.requireNonNull(ar.getBody()).getData();
page++;
return res;
}
}