使用Java 8流,在单次遍历中计算多个项目

5

Suppose I have the following class:

class Z {
    X x;
    Y y;
}

我有一个Z元素列表。我想在一次遍历中计算有多少元素的x字段的值是x1,以及有多少个元素的y字段的值是y1。

使用循环可以很容易地实现:

int countOfx1 = 0;
int countOfy1 = 0;
for (Z z: list) {
    if (z.x == x1) {
        countOfx1++
    }
    if (z.y == y1) {
        countOfy1++
    }
 }

能否仅使用流来完成?
2个回答

7
你可以通过创建一个用于总计的收集器来实现这一点:
class Zcount {
    private int xCount = 0;
    private int yCount = 0;

    public Zcount accept(Z z) {
        if (z.x == x1)
            xCount++;
        if (z.y == y1)
            yCount++;
        return this;
    }

    public Zcount combine(ZCount other) {
        xCount += other.xCount;
        yCount += other.yCount;
        return this;
    }
}

Zcount count = list.stream().collect(Zcount::new, Zcount::accept, Zcount::combine);

相较于迭代解决方案,这个方案的优势在于如果列表非常大,你可以将流并行处理,这样可能会有更好的性能表现。


谢谢!当你需要并行处理时,这非常方便。我通常习惯于将Steam.collect()与集合相关联。但你向我展示了它可以用于更广泛的上下文。 - Shay

3

您可以使用我在此答案中发布的multiClassify收集器:

List<Predicates> preds = Arrays.asList(z -> z.x == x1, z -> z.y == y1);
List<Long> counts = stream.collect(multiClassify(preds, Collectors.counting()));
// counts.get(0) -> counts for z.x == x1
// counts.get(1) -> counts for z.y == y1

当然,简单的替代方案是对输入进行两次遍历:
long countsX = list.stream().filter(z -> z.x == x1).count();
long countsY = list.stream().filter(z -> z.y == y1).count();

这种解决方案很简短,对于如ArrayList这样的常规输入,性能通常不会太差。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接