Reputation: 474
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?
Upvotes: 6
Views: 2068
Reputation: 100309
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
.
Upvotes: 3
Reputation: 27986
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.
Upvotes: 8