英文:
combine multiple Mono<List<Item>> into one
问题
I'm currently working on a project that involves a bit of reactive programming.
我目前正在进行一个涉及反应式编程的项目。
I have 4 different reactive repositories from which I get 4 different Mono<List<SomeType>>
in return respectively.
我有4个不同的反应式存储库,分别从中获取4个不同的Mono<List<SomeType>>
。
The goal is to combine them into a single Mono<List<GeneralType>>
in order to incorporate that into a custom Response to return within a ResponseEntity.ok()
.
目标是将它们合并成一个单一的Mono<List<GeneralType>>
,以便将其包含在自定义响应中返回到ResponseEntity.ok()
。
All repositories have a similar signature:
所有存储库都具有相似的签名:
public Mono<List<SomeType>> findAllByUserId(UUID userId)
The field in my Response that incorporates all different lists as a single one:
在我的响应中,将所有不同的列表合并为一个的字段:
private Mono<List<GeneralType>> items;
What my method looks like so far:
目前我的方法看起来像这样:
Mono<List<GeneralType>> combo1 = reactiveRepository.findAllByUserId(userId)
.map(list -> list.stream()
.map(GeneralType::new)
.collect(Collectors.toList()));
return combo1; // works just fine
}```
All the other lists have pretty much the same approach, but putting them together into a single Mono<List<GeneralType>> is a problem.
所有其他列表基本上采用相同的方法,但将它们合并成一个单一的Mono<List<GeneralType>>是一个问题。
I've tried the following:
我尝试过以下方法:
```return Flux.merge(combo1.flatMapMany(Flux::fromIterable), combo2.flatMapMany(Flux::fromIterable)).collectList();```
But with that, the IDE urges to change the return type to ```Flux<Object>```.
但是,这样做时,IDE 建议将返回类型更改为```Flux<Object>```。
Also, some lists can be empty, so I'm not sure if ```zip()``` is an option here. I've read that it will return everything as *empty* if at least a single result is empty.
而且,有些列表可能为空,所以我不确定```zip()```是否是一个选项。我读过,如果至少有一个结果为空,它会将所有内容都返回为空。
So the question is **how can that be done in an efficient way without *block()* everywhere?**
因此,问题是**如何以高效的方式完成这个任务,而不需要在各处使用*block()*?**
<details>
<summary>英文:</summary>
I'm currently working on a project that involves a bit of reactive programming.
I have 4 different reactive repositories from which I get 4 different ```Mono<List<SomeType>>``` in return respectively.
The goal is to **combine them into a single ```Mono<List<GeneralType>>```** in order to incorporate that into a custom Response to return within a ```ResponseEntity.ok()```. I have already taken care of creating a ```GeneralType``` and was successful in converting a single ```Mono<List<SomeType>>```, however, made no further progress.
All repositories have a similar signature:
public Mono<List<SomeType>> findAllByUserId(UUID userId)
The field in my Response that incorporates all different lists as a single one:
private Mono<List<GeneralType>> items;
What my method looks like so far:
public Mono<List<GeneralType>> combineMonos(UUID userId) {
Mono<List<GeneralType>> combo1 = reactiveRepository.findAllByUserId(userId)
.map(list -> list.stream()
.map(GeneralType::new)
.collect(Collectors.toList()));
return combo1; // works just fine
}
All the other lists have pretty much the same approach, but putting them together into a single Mono<List<GeneralType>> is a problem.
I've tried the following:
return Flux.merge(combo1.flatMapMany(Flux::fromIterable), combo2.flatMapMany(Flux::fromIterable)).collectList();
But with that, the IDE urges to change the return type to ```Flux<Object>```.
Also, some lists can be empty, so I'm not sure if ```zip()``` is an option here. I've read that it will return everything as *empty* if at least a single result is empty.
So the question is **how can that be done in an efficient way without *block()* everywhere?**
</details>
# 答案1
**得分**: 5
**Merge**会立即连接到所有数据源。因此,只要来自任何数据源的数据被发射,它将被传递到下游管道。结果列表中的顺序基于项目发射的时间。
**Zip**方法会收集来自各个源的数据并将它们放入一个对象(元组 - 类似于一个盒子),然后传递给下游。只要所有源都发射数据,Zip就会工作。如果任何一个源完成或抛出错误,它将停止工作。
<details>
<summary>英文:</summary>
**Merge** connects to all the data sources eagerly. So as and when data is emitted from the any of the sources, it would be passed to the downstream pipeline. Order in the resulting list is based on when the item was emitted.
**Zip** method collects the data from sources and places them inside an object (Tuple – something like a box) and passes to the downstream. Zip will work as long as all the sources emit data. Any of the sources completes/throws error, it will stop.
----
I assumed your individual methods are working fine. Your question is related to merging the results into a single list.
private Mono<List<String>> getList1(){
return Mono.just(List.of("a", "b", "c"));
}
private Mono<List<String>> getList2(){
return Mono.just(Collections.emptyList());
}
private Mono<List<String>> getList3(){
return Mono.just(List.of("A", "B", "C"));
}
Flux.merge(getList1(), getList2(), getList3())
.flatMapIterable(Function.identity())
.collectList()
.subscribe(System.out::println); // [a, b, c, A, B, C]
----
Reference:
http://www.vinsguru.com/reactive-programming-reactor-combining-multiple-sources-of-flux-mono/
</details>
# 答案2
**得分**: 1
A `Mono::zip`将异步组合这三个发布者,我认为这是最好的解决方案。
否则,这是一个相当简单的问题:
```java
Mono<List<String>> m1 = Mono.just(Arrays.asList(new String[]{"A", "B", "C"}));
Mono<List<Character>> m2 = Mono.just(Arrays.asList(new Character[]{'a', 'b', 'c'}));
Mono<List<Integer>> m3 = Mono.just(Arrays.asList(new Integer[]{1, 2, 3}));
Mono.zip(m1, m2, m3)
.map(tuple3 -> {
List<Combined> c = new ArrayList<>();
int size = tuple3.getT1().size();
for (int i = 0; i < size; ++i) {
c.add(new Combined(tuple3.getT1().get(i), tuple3.getT2().get(i), tuple3.getT3().get(i)));
}
return c;
})
.subscribe(System.out::println);
// [Combined(s=A, c=a, i=1), Combined(s=B, c=b, i=2), Combined(s=C, c=c, i=3)]
为了完整起见:
@Data
@AllArgsConstructor
class Combined {
String s;
Character c;
Integer i;
}
英文:
A Mono::zip
will asynchronously combine the three publishers together which I think is the best solution.
Otherwise it is a pretty straightforward problem:
Mono<List<String>> m1 = Mono.just(Arrays.asList(new String[]{"A", "B", "C"}));
Mono<List<Character>> m2 = Mono.just(Arrays.asList(new Character[]{'a', 'b', 'c'}));
Mono<List<Integer>> m3 = Mono.just(Arrays.asList(new Integer[]{1, 2, 3}));
Mono.zip(m1, m2, m3)
.map(tuple3->{
List<Combined> c = new ArrayList<>();
int size = tuple3.getT1().size();
for ( int i=0; i < size; ++i ) {
c.add(new Combined(tuple3.getT1().get(i), tuple3.getT2().get(i), tuple3.getT3().get(i)));
}
return c;
})
.subscribe(System.out::println);
// [Combined(s=A, c=a, i=1), Combined(s=B, c=b, i=2), Combined(s=C, c=c, i=3)]
For completeness sake:
@Data
@AllArgsConstructor
class Combined {
String s;
Character c;
Integer i;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论