RxJava- Group,Emit和Zip将具有共同属性的“Chunks”排序?

时间:2015-07-20 18:35:53

标签: java reactive-programming rx-java

我和我的同事经常遇到挑战,我希望被动编程可以解决它。它可能需要我自己实现OperatorTransformer

我想接受Observable<T>TT项,但我希望操作员将它们分组到List<T>的映射上,并将每个分组发送为Collector,甚至更好的一些通用累加器,就像Java 8流中的groupBy()一样。

但这是我认为T无法做到的棘手部分。我想让两个Observable通过这个运算符,并假设发出的项目在该属性上排序(传入的数据将从排序的SQL查询中发出并映射到PARTITION_ID对象)。操作员将连续累积项目直到属性更改,然后它将发出该组并继续进行下一个项目。这样我就可以从每个Observable中获取每组数据,压缩并处理这两个块,然后扔掉它们继续前进到下一个块。这样我可以保持半缓冲状态并保持低内存使用率。

因此,如果我对public final class SortedPartitioner<T,P,C,R> implements Transformer<T,R> { private final Function<T,P> mappedPartitionProperty; private final Supplier<C> acculatorSupplier; private final BiConsumer<T,R> accumulator; private final Function<C,R> finalResult; private SortedPartitioner(Function<T, P> mappedPartitionProperty, Supplier<C> acculatorSupplier, BiConsumer<T, R> accumulator, Function<C, R> finalResult) { this.mappedPartitionProperty = mappedPartitionProperty; this.acculatorSupplier = acculatorSupplier; this.accumulator = accumulator; this.finalResult = finalResult; } public static <T,P,C,R> SortedPartitioner<T,P,C,R> of( Function<T,P> mappedPartitionProperty, Supplier<C> accumulatorSupplier, BiConsumer<T,R> accumulator, Function<C,R> finalResult) { return new SortedPartitioner<>(mappedPartitionProperty, accumulatorSupplier, accumulator, finalResult); } @Override public Observable<R> call(Observable<T> t) { return null; } } 进行排序,分组和压缩,这在视觉上是我想要完成的。

enter image description here

我只是这样做,因为我可以有两个查询,每个查询超过一百万条,我需要并排进行复杂的比较。我没有内存可以同时从双方导入所有数据,但我可以将其范围缩小到每个已排序的属性值并将其分解为批次。每批后,GC将丢弃它,操作员可以继续下一个。

这是我到目前为止的代码,但我有点不清楚如何继续,因为我不希望在批处理完成之前发出任何东西。我到底该怎么做?

<xsl:output>

2 个答案:

答案 0 :(得分:1)

这是一个棘手的问题,但我经常遇到一个。

诀窍是使用materializescanflatMapscan累积具有相同partitionId的值列表,如果存在,则累积下一个不同的值。需要materialize,因为我们需要知道源何时完成,因此如果存在,我们可以向左发送不同的值。 flatMap获取列表和值,并在值存在时发出列表(我们刚刚切换到新的partitionId)并在流完成时发出值(剩余)。

以下是列表1, 1, 2, 2, 2, 3列出{1, 1}, {2, 2, 2}, {3}列表的单元测试。

对于您的用例,您只需将此技术应用于两个源并将它们压缩在一起。

代码:

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.junit.Test;

import rx.Observable;

public class StateMachineExampleTest {

    @Test
    public void testForStackOverflow() {
        Observable<Integer> a = Observable.just(1, 1, 2, 2, 2, 3);
        State<Integer> initial = new State<Integer>(Collections.emptyList(), Optional.empty(),
                false);
        List<List<Integer>> lists = a.materialize()
                // accumulate lists and uses onCompleted notification to emit
                // left overs when source completes
                .scan(initial,
                        (state, notification) -> {
                            if (notification.isOnCompleted()) {
                                return new State<>(null, state.value, true);
                            } else if (notification.isOnError())
                                throw new RuntimeException(notification.getThrowable());
                            else if (state.list.size() == 0) {
                                return new State<>(Arrays.asList(notification.getValue()), Optional
                                        .empty(), false);
                            } else if (partitionId(notification.getValue()) == partitionId(state.list
                                    .get(0))) {
                                List<Integer> list = new ArrayList<>();
                                list.addAll(state.list);
                                list.add(notification.getValue());
                                return new State<>(list, Optional.empty(), false);
                            } else if (state.value.isPresent()) {
                                if (partitionId(state.value.get()) == partitionId(notification
                                        .getValue())) {
                                    return new State<>(Arrays.asList(state.value.get(),
                                            notification.getValue()), Optional.empty(), false);
                                } else {
                                    return new State<>(Arrays.asList(state.value.get()), Optional
                                            .of(notification.getValue()), false);
                                }
                            } else {
                                return new State<>(state.list,
                                        Optional.of(notification.getValue()), false);
                            }
                        })
                // emit lists from state
                .flatMap(state -> {
                    if (state.completed) {
                        if (state.value.isPresent())
                            return Observable.just(Arrays.asList(state.value.get()));
                        else
                            return Observable.empty();
                    } else if (state.value.isPresent()) {
                        return Observable.just(state.list);
                    } else {
                        return Observable.empty();
                    }
                })
                // get as a list of lists to check
                .toList().toBlocking().single();
        assertEquals(Arrays.asList(Arrays.asList(1, 1), Arrays.asList(2, 2, 2), Arrays.asList(3)),
                lists);
    }

    private static int partitionId(Integer n) {
        return n;
    }

    private static final class State<T> {
        final List<T> list;
        final Optional<T> value;
        final boolean completed;

        State(List<T> list, Optional<T> value, boolean completed) {
            this.list = list;
            this.value = value;
            this.completed = completed;
        }
    }

}

请记住,此代码很快被破坏,可能会有漏洞。请务必使用此代码进行完整的单元测试。

您需要注意的是,由于我们使用背压支持操作符materializescanflatMap,因此生成的转换也支持背压,因此可以安全地与{{1}结合使用}。

答案 1 :(得分:1)

另一个答案是在Maven Central上使用库而且更短。

将此依赖项添加到pom.xml

<dependency>
    <groupId>com.github.davidmoten</groupId>
    <artifactId>rxjava-extras</artifactId>
    <version>0.5.13</version>
</dependency>

在对具有相同partition_id的项目进行分组方面,请执行以下操作:

import com.github.davidmoten.rx.Transformers;

Observable<List<Item>> grouped = items.compose(
    Transformers.toListWhile(
        (list, item) -> list.isEmpty() || list.get(0).partitionId == item.partitionId));

此方法的测试非常全面(另请参阅Transformers.collectWhile以了解列表以外的数据结构),但您可以在github上自行检查来源。

然后继续zip