我和我的同事经常遇到挑战,我希望被动编程可以解决它。它可能需要我自己实现Operator
或Transformer
。
我想接受Observable<T>
个T
个T
项,但我希望操作员将它们分组到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;
}
}
进行排序,分组和压缩,这在视觉上是我想要完成的。
我只是这样做,因为我可以有两个查询,每个查询超过一百万条,我需要并排进行复杂的比较。我没有内存可以同时从双方导入所有数据,但我可以将其范围缩小到每个已排序的属性值并将其分解为批次。每批后,GC将丢弃它,操作员可以继续下一个。
这是我到目前为止的代码,但我有点不清楚如何继续,因为我不希望在批处理完成之前发出任何东西。我到底该怎么做?
<xsl:output>
答案 0 :(得分:1)
这是一个棘手的问题,但我经常遇到一个。
诀窍是使用materialize
,scan
和flatMap
。 scan
累积具有相同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;
}
}
}
请记住,此代码很快被破坏,可能会有漏洞。请务必使用此代码进行完整的单元测试。
您需要注意的是,由于我们使用背压支持操作符materialize
,scan
和flatMap
,因此生成的转换也支持背压,因此可以安全地与{{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
。