Java 8 流 API - 在分组后选择最低键

2022-09-02 23:58:41

我有一个 Foo 对象流。

class Foo {
    private int variableCount;
    public Foo(int vars) {
        this.variableCount = vars; 
    }
    public Integer getVariableCount() { 
      return variableCount; 
    }
}

我想要一个列表,它们都具有最低的变量Count。Foo

例如

new Foo(3), new Foo(3), new Foo(2), new Foo(1), new Foo(1)

我只希望流返回最后2秒,因为它们具有最低值。Foo

我尝试过按以下方式进行分组收集

.collect(Collectors.groupingBy((Foo foo) -> {
                    return foo.getVariableCount();
})

这又返回了一个,我不知道如何把它变成我想要的。Map<Integer, List<Foo>>

提前致谢


答案 1

您可以使用排序地图进行分组,然后只获取第一个条目。大致内容如下:

Collectors.groupingBy(
    Foo::getVariableCount,
    TreeMap::new,
    Collectors.toList())
.firstEntry()
.getValue()

答案 2

以下是以下解决方案:

  1. 仅流式传输列表一次。
  2. 不构建包含所有输入项的映射或其他结构(除非变量计数都相同),仅保留当前最小值的映射或其他结构。
  3. 是 O(n) 时间,O(n) 空间。完全有可能所有 s 都具有相同的变量计数,在这种情况下,此解决方案将像其他解决方案一样存储所有项目。但实际上,由于值不同且基数较高,列表中的项目数可能会低得多。Foo

编辑

我根据评论中的建议改进了我的解决方案。

我实现了一个累加器对象,它为此提供函数。Collector

/**
 * Accumulator object to hold the current min
 * and the list of Foos that are the min.
 */
class Accumulator {
    Integer min;
    List<Foo> foos;

    Accumulator() {
        min = Integer.MAX_VALUE;
        foos = new ArrayList<>();
    }

    void accumulate(Foo f) {
        if (f.getVariableCount() != null) {
            if (f.getVariableCount() < min) {
                min = f.getVariableCount();
                foos.clear();
                foos.add(f);
            } else if (f.getVariableCount() == min) {
                foos.add(f);
            }
        }
    }

    Accumulator combine(Accumulator other) {
        if (min < other.min) {
            return this;
        }
        else if (min > other.min) {
            return other;
        }
        else {
            foos.addAll(other.foos);
            return this;
        }
    }

    List<Foo> getFoos() { return foos; }
}

那么我们所要做的就是,引用累加器的方法来实现其函数。collect

List<Foo> mins = foos.stream().collect(Collector.of(
    Accumulator::new,
    Accumulator::accumulate,
    Accumulator::combine,
    Accumulator::getFoos
    )
);

测试

List<Foo> foos = Arrays.asList(new Foo(3), new Foo(3), new Foo(2), new Foo(1), new Foo(1), new Foo(4));

输出是(在 上定义了合适的):toStringFoo

[Foo{1}, Foo{1}]

推荐