Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Count in single pass multiple items using java 8 stream

Suppose I have the following class:

class Z {
    X x;
    Y y;
}

And I have a list of Z elements. I want to count in a single pass how many elements have in their x field the value x1, and how many have in their y field the value y1.

Using a loop it is straight forward:

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

Can it be done as simply using streams?

like image 991
Shay Avatar asked Jan 06 '23 14:01

Shay


2 Answers

You can do this by creating a collector for the totals:

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);

This has the advantage over the iterative solution that you can make the stream parallel which could have performance advantages if your list is very large.

like image 88
sprinter Avatar answered Jan 08 '23 02:01

sprinter


You can use multiClassify collector which I posted in this answer:

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

The simple alternative is, of course, to traverse the input twice:

long countsX = list.stream().filter(z -> z.x == x1).count();
long countsY = list.stream().filter(z -> z.y == y1).count();

Such solution is short and usually not so bad in terms of performance for usual inputs like ArrayList.

like image 22
Tagir Valeev Avatar answered Jan 08 '23 04:01

Tagir Valeev