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?
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With