与GROUP BY相当于COUNT的Rx?

时间:2016-09-15 01:03:36

标签: rx-java reactive-programming

我有一系列重复元素,比如说:

Observable<String> source = Observable.just("A", "B", "A", "C", "C", "A");

我想按照它们的值以及它们出现的次数对它们进行分组,因此输出将成对:

{"A", 3}, {"B", 1}, {"C", 2}

基本上等同于SELECT x, COUNT(1) GROUP BY x;

之类的SQL语句

我到目前为止只打电话给groupBy:

source.groupBy(x -> x, x -> 1)

但是这会将流转换为GroupedObservables,而我找不到一个如何继续使用它们的好例子。我尝试了reduce(),但这里并不好,因为在groupBy()之后它想要减少GroupedObservables,而不是每个组内的元素。

GroupedObservables可以实现吗?有没有其他方法可以达到预期的效果?

3 个答案:

答案 0 :(得分:11)

以下代码:

source.groupBy(val -> val)
    .flatMap(
        gr -> gr.count()
                .map(count -> new Pair<>(gr.getKey(), count)
    )
).subscribe(System.out::println);

打印出来:

A=3
B=1
C=2

答案 1 :(得分:1)

    Observable<String> source = Observable.just("A", "B", "A", "C", "C", "A");
    Observable<KeyValue<String, Integer>> countStream = source
            .groupBy(val -> val)
            .flatMap(obs -> obs.count().flatMap(cnt -> Observable.just(new KeyValue<>(obs.getKey(), cnt))));

  private static class KeyValue<K, V> {

    private final K key;
    private final V val;

    public KeyValue(K key, V val) {
        this.key = key;
        this.val = val;
    }

    public K getKey() {
        return key;
    }

    public V getVal() {
        return val;
    }
}

答案 2 :(得分:1)

另一种方法是使用collectcollectInto方法,如下所示。

Observable<String> source = Observable.just("A", "B", "A", "C", "C", "A");

source.collectInto(new HashMap<String, MutableInt>(), (map, elem) -> {
    if (map.containsKey(elem)) {
    map.get(elem).increment();
    } else {
    map.put(elem, new MutableInt(1));
    }

}).subscribe(System.out::println);

顺便说一下,如果我们使用Reactorcollect是这样做的暗示方式,因为在大量组的情况下,groupBy之后的flatMap将挂起。

来自javadoc of Reactor

  

请注意,groupBy最适用于基数较低的基数,因此请相应地选择keyMapper函数   ...
  值得注意的是,当标准产生大量组时,如果组不适合在下游消耗,则可能导致挂起(例如,由于具有设置得太低的maxConcurrency参数的flatMap)。

还有一个与此相关的github issue