简体   繁体   English

RxJava - 整合多个、无限的 Observable <List<T> &gt;?

[英]RxJava- Consolidating multiple, infinite Observable<List<T>>?

Here is a fun little RxJava puzzle I am dealing with.这是我正在处理的一个有趣的 RxJava 小谜题。 Let's say I have an Observable<List<Parent>> infiniteParentListStream that is infinite, and each Parent has an Observable<List<Child>> infiniteChildListStream property which also is infinite.假设我有一个Observable<List<Parent>> infiniteParentListStream是无限的,每个Parent都有一个Observable<List<Child>> infiniteChildListStream属性,它也是无限的。

I want to take all Parent instances in the emitted List<Parent> , and consolidate each of their emitted List<Child> items into a single, whole List<Child> reflecting all children of all parents.我想获取发出的List<Parent>中的所有Parent实例,并将它们发出的每个List<Child>项目合并为一个完整的List<Child>反映所有父项的所有子项。

The fact the Observable<List<Child>> infiniteChildListStream property in Parent is infinite is making the toList() task a little challenging.事实上, ParentObservable<List<Child>> infiniteChildListStream属性是无限的,这使得toList()任务有点挑战。

public final class NestedInfiniteTest {

    private static final BehaviorSubject<Integer> parentSubject = BehaviorSubject.create(1);
    private static final BehaviorSubject<Integer> childSubject = BehaviorSubject.create(1);

    public static void main(String[] args) {


        Observable<List<Parent>> infiniteParentListStream = parentSubject
                .map(i -> Arrays.asList(new Parent(), new Parent(), new Parent()))
                .cache(1);

        Observable<List<Child>> allCurrentChildren = infiniteParentListStream.<List<Child>>flatMap(parentList ->
                Observable.from(parentList)
                        .flatMap(p -> p.getInfiniteChildListStream().flatMap(Observable::from)).toList()
        ).cache(1);

        allCurrentChildren.subscribe(cl -> System.out.println("WHOLE CHILD LIST SIZE: " + cl.size()));
    }

    private static final class Parent {
        private final Observable<List<Child>> infiniteChildListStream = childSubject
                .map(i -> Arrays.asList(new Child(), new Child(), new Child())).cache(1);

        public Observable<List<Child>> getInfiniteChildListStream() {
            return infiniteChildListStream;
        }
    }
    private static final class Child {

    }
}

One workaround solution I found, of course, is to turn the infiniteChildListStream finite by calling first() .当然,我找到的一种解决方法是通过调用first()来将infiniteChildListStream变为有限。 But this is less than desirable since it no longer updates.但这并不理想,因为它不再更新。

Observable<List<Child>> allCurrentChildren = infiniteParentListStream.<List<Child>>flatMap(parentList ->
        Observable.from(parentList)
                .flatMap(p -> p.getInfiniteChildListStream().first().flatMap(Observable::from)).toList()
).cache(1);

I feel like there is a way to manually call Observable.create() or use flatMap() tricks to resolve this.我觉得有一种方法可以手动调用Observable.create()或使用flatMap()技巧来解决这个问题。 Is there a better way to do this and to keep things reactive with the infinite sources?有没有更好的方法来做到这一点并使事物与无限源保持反应? In my real application outside this SSCCE, these observables are infinite because the data sources driving Parent and Child could change and emit new values...在我这个 SSCCE 之外的实际应用程序中,这些 observables 是无限的,因为驱动ParentChild的数据源可能会改变并发出新的值......

I guess the root of my question is how do I take multiple infinite Observable<List<T>> and consolidate them to a single Observable<List<T>> ?我想我的问题的根源是如何获取多个无限Observable<List<T>>并将它们合并为一个Observable<List<T>>

I think I figured it out by using Observable.combineLatest() .我想我是通过使用Observable.combineLatest()弄清楚的。 To enhance testing, I also modified the source observables to create varying List sizes based on the subject's pushed integer value.为了增强测试,我还修改了源 observables 以根据主题的推送整数值创建不同的List大小。 This looks like it works beautifully.这看起来很漂亮。

public final class NestedInfiniteTest {

    private static final BehaviorSubject<Integer> parentSubject = BehaviorSubject.create(1);
    private static final BehaviorSubject<Integer> childSubject = BehaviorSubject.create(1);

    public static void main(String[] args) {

        Observable<List<Parent>> infiniteParentListStream = parentSubject
                .map(i -> IntStream.range(0,i).mapToObj(val -> new Parent()).collect(Collectors.toList()))
                .cache(1);

        Observable<List<Child>> allCurrentChildren = infiniteParentListStream.flatMap(parentList ->
                Observable.<Observable<List<Child>>>create(s -> {
                    parentList.stream().map(Parent::getInfiniteChildListStream).forEach(s::onNext);
                    s.onCompleted();
                })
                .toList() //List<<Observable<List<Child>>>>
                .flatMap(consolidatedChildList -> Observable.combineLatest(consolidatedChildList, new FuncN<List<Child>>() {
                    @Override
                    public List<Child> call(Object... args) {
                        ArrayList<Child> list = new ArrayList<>();
                        for (Object obj : args) {
                            list.addAll((List<Child>) obj);
                        }
                        return list;
                    }
                }))
        );


        allCurrentChildren.subscribe(cl -> System.out.println("WHOLE CHILD LIST SIZE: " + cl.size()));
        childSubject.onNext(10);
        parentSubject.onNext(5);
        childSubject.onNext(2);
    }

    private static final class Parent {
        private final Observable<List<Child>> infiniteChildListStream = childSubject
                .map(i -> IntStream.range(0, i).mapToObj(val -> new Child()).collect(Collectors.toList())).cache(1);

        public Observable<List<Child>> getInfiniteChildListStream() {
            return infiniteChildListStream;
        }
    }
    private static final class Child {

    }
}

OUTPUT:输出:

WHOLE CHILD LIST SIZE: 1   //parentSubject = 1, childSubject = 1
WHOLE CHILD LIST SIZE: 10  //parentSubject = 1, childSubject = 10
WHOLE CHILD LIST SIZE: 50  //parentSubject = 5, childSubject = 10
WHOLE CHILD LIST SIZE: 2   //parentSubject = 5, childSubject = 2, adjusting
WHOLE CHILD LIST SIZE: 42  //adjusting
WHOLE CHILD LIST SIZE: 34  //adjusting
WHOLE CHILD LIST SIZE: 26  //adjusting
WHOLE CHILD LIST SIZE: 18  //adjusting
WHOLE CHILD LIST SIZE: 10  //parentSubject = 5, childSubject = 2, done!

UPDATE: Created a Transformer to perform this task更新:创建了一个变压器来执行这个任务

public static class CombinedListTransformer<T,R> implements Observable.Transformer<List<T>,List<R>> {

    private final Func1<T,Observable<List<R>>> listMapper;

    public CombinedListTransformer(Func1<T,Observable<List<R>>> listMapper) {
        this.listMapper = listMapper;
    }
    @Override
    public Observable<List<R>> call(Observable<List<T>> sourceList) {
        return sourceList.flatMap(sl ->
            Observable.from(sl).map(t -> listMapper.call(t)).toList() //List<Observable<List<R>>
            .flatMap(consolidatedChildList -> Observable.combineLatest(consolidatedChildList, args -> {
                ArrayList<R> list = new ArrayList<>();
                for (Object obj : args) {
                    list.addAll((List<R>) obj);
                }
                return list;
            }))
        );
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM