Java 如何在不使用reduce()或last()折叠通量的情况下获取通量的最后一项

Java 如何在不使用reduce()或last()折叠通量的情况下获取通量的最后一项,java,project-reactor,reactive-streams,Java,Project Reactor,Reactive Streams,如何在不使用reduce()或last()折叠通量的情况下获取通量的最后一项?以下是我的用例: 1) 我有一个基于状态产生通量的发生器。 2) 当内部通量完成时,它会改变影响生成器中发射的下一个通量对象的状态 示意图是这样的 static class State { int secret = 2; int iteration = 0; } Random rand = new Random(1024); Flux<Integer> stream = Flux.<

如何在不使用reduce()或last()折叠通量的情况下获取通量的最后一项?以下是我的用例:

1) 我有一个基于状态产生通量的发生器。 2) 当内部
通量
完成时,它会改变影响生成器中发射的下一个
通量
对象的状态

示意图是这样的

static class State {
    int secret = 2;
    int iteration = 0;
}

Random rand = new Random(1024);
Flux<Integer> stream = Flux.<Flux<Integer>, State>generate(State::new, (state, sink) -> {

    System.out.println(String.format("Generate: %d", state.secret));
    Flux<Integer> inner = Flux.range(1, rand.nextInt(10));

    sink.next(inner.doOnComplete(() -> {
        // How do I get last item of `inner` here ?
        // For example I'd like to decrement `state.secret` by last value of `inner`
    }));

    return state;
}).flatMap(Function.identity());
静态类状态{
int秘密=2;
int迭代=0;
}
随机随机数=新随机数(1024);
流量流=流量。生成(状态::新建,(状态,接收器)->{
System.out.println(String.format(“生成:%d”,state.secret));
内通量=通量范围(1,rand.nextInt(10));
sink.next(内部的doOnComplete(()->{
//我怎样才能在这里得到“内部”的最后一项?
//例如,我想用'inner'的最后一个值来递减'state.secret'`
}));
返回状态;
}).flatMap(Function.identity());

UPD:我没有标记我的答案,因为黑客被证明是不可靠的。有可能在前一个
流量
完全消耗之前调用
.generate()
,因此使
最后一个
中的值不正确。

第一个版本不可靠。我又砍了一个:

static <T> Flux<T> expandOnLastItem(Supplier<Flux<T>> seed, Function<T, Flux<T>> generator) {
    return Flux.just(new AtomicReference<T>())
            .flatMap(last -> Flux.just(seed.get().materialize())
                    .flatMap(Function.identity())
                    .expand(v -> {
                        if (v.hasValue()) {
                            last.set(v.get());
                        } else if (v.isOnComplete() && last.get() != null) {
                            Flux<T> res = generator.apply(last.get());
                            last.set(null);
                            return res.materialize();
                        }
                        return Flux.empty();
                    })
                    .filter(s -> !s.isOnComplete())
                    .dematerialize());
}
静态流量扩展项(供应商种子、函数生成器){
返回Flux.just(新的AtomicReference())
.flatMap(last->Flux.just(seed.get().materialize())
.flatMap(函数.identity())
.展开(v->{
if(v.hasValue()){
last.set(v.get());
}else if(v.isOnComplete()&&last.get()!=null){
Flux res=生成器.apply(last.get());
last.set(null);
return res.materialize();
}
返回通量.empty();
})
.filter(s->!s.isOnComplete())
.dematerialize());
}
可以用作

static Flux<Integer> getPage(int pageId, int size) {
    return Flux.defer(() -> {
        if (pageId < 3) {
            System.out.println("Returning data for pageId: " + pageId);
            return Flux.range(pageId * 100, size);
        } else {
            System.out.println("Returning empty for pageId: " + pageId);
            return Flux.empty();
        }
    });
}

expandOnLastItem(
        () -> getPage(0, 5),
        lastId -> {
            System.out.println("  Expanding. Last item: " + lastId);
            int curPage = lastId / 100;
            return getPage(curPage + 1, 5);
        })
        .reduce(0L, (count, value) -> {
            System.out.println("==> " + value);
            return count + 1;
        })
        .block();
static Flux getPage(int pageId,int size){
返回流量延迟(()->{
如果(页面ID<3){
System.out.println(“返回pageId:+pageId的数据”);
返回流量范围(pageId*100,大小);
}否则{
System.out.println(“为pageId:+pageId返回空值”);
返回通量.empty();
}
});
}
膨胀镜(
()->getPage(0,5),
lastId->{
System.out.println(“扩展.最后一项:+lastId”);
int curPage=lastId/100;
返回getPage(curPage+1,5);
})
.减少(0升,(计数,值)->{
System.out.println(“==>”+值);
返回计数+1;
})
.block();

所以我通过改变生成器中的状态变量来破解它。它可以工作,但不是很实用。如果有人能提出替代方案,我将不胜感激

Random rand = new Random(1024);
Flux.<Flux<String>, State>generate(State::new, (state, sink) -> {

    if (state.iteration < 4) {
        final int count = rand.nextInt(10) + 1;
        System.out.println(String.format("*** Generate %d: start %d (count %d)", state.iteration, state.secret, count));
        Flux<Integer> inner = Flux.range(state.secret, count);

        final int[] last = {Integer.MIN_VALUE};
        sink.next(
                inner
                        .doOnNext(value -> {
                            last[0] = value;
                        })
                        .map(value -> String.format("Iter %d value %d", state.iteration, value))
                        .doOnComplete(() -> {
                            System.out.println(String.format("Inner complete (last item was %d)", last[0]));
                            state.secret = last[0];
                            state.iteration += 1;
                        }));
    } else {
        System.out.println("Generate complete");
        sink.complete();
    }

    return state;
})
        .flatMap(Function.identity())
        .map(value -> {
            System.out.println(String.format("Ext map: %s", value));
            return value;
        })
        .buffer(5)
        .flatMapIterable(Function.identity())
        .subscribe(value -> System.out.println(String.format("  ---> %s", value)));

System.out.println("Exiting");
Random rand=new Random(1024);
Flux.generate(State::new,(State,sink)->{
if(state.iteration<4){
最终整数计数=随机数(10)+1;
System.out.println(String.format(“***生成%d:开始%d(计数%d)”,state.iteration,state.secret,count));
通量内部=通量范围(国家机密,计数);
final int[]last={Integer.MIN_VALUE};
下一个(
内部的
.doOnNext(值->{
最后[0]=值;
})
.map(值->字符串.format(“Iter%d值%d”,state.iteration,value))
.doOnComplete(()->{
System.out.println(String.format(“内部完成(最后一项是%d)”,最后一个[0]);
state.secret=last[0];
state.iteration+=1;
}));
}否则{
System.out.println(“生成完成”);
sink.complete();
}
返回状态;
})
.flatMap(函数.identity())
.map(值->{
System.out.println(String.format(“Ext-map:%s”,value));
返回值;
})
.缓冲区(5)
.flatMapIterable(函数.identity())
.subscribe(value->System.out.println(String.format(“-->%s”,value));
系统输出打印项次(“退出”);

您能否在内部流量上调用
share()
以允许多个订阅者,这将使您能够在没有问题的情况下调用
last()
?@MichaelBerry是否会增加流开始时丢失项目的风险,因为源现在很热?可能是的。如果你能切实地
cache()
整个事情,那么它就没那么重要了,但是如果不是(缺少元素也很重要),那么这就不是我推荐的方法。因为我现在没有什么东西可以测试它。你想试试这条小溪的具体化吗?因为它会将其转换为信号+上下文,所以它可能在“完整”信号中有最后一个值。变异变量是我唯一想到的“快速”方式,但我不想回答这个问题,因为它是。。。哈奇,就像你演示的那样!至少我会在这里使用
AtomicInteger
,而不是原始数组。通过将“黑客”的东西封装在一个单独的处理器中,跟踪最后发出的值,您可能可以使它变得更好,但它仍然在做本质上相同的事情。如果我想一个更好的解决方案,我会把它贴出来。@MichaelBerry你能想到当它崩溃时的场景吗?例如,下游请求的项目是否可能多于内部流中的可用项目,从而在第一个内部流完成之前触发新的generate调用(并导致
last[0]
没有最后一个元素)?