Let me reformat your example a bit for the sake of discussion:
long runCount = 0L;
myStream.stream()
.filter(...)
.forEach(item -> {
foo();
bar();
runCount++; // doesn't work
});
System.out.println("The lambda ran " + runCount + " times");
If you really need to increment a counter from within a lambda, the typical way to do so is to make the counter an AtomicInteger
or AtomicLong
and then call one of the increment methods on it.
You could use a single-element int
or long
array, but that would have race conditions if the stream is run in parallel.
But notice that the stream ends in forEach
, which means that there is no return value. You could change the forEach
to a peek
, which passes the items through, and then count them:
long runCount = myStream.stream()
.filter(...)
.peek(item -> {
foo();
bar();
})
.count();
System.out.println("The lambda ran " + runCount + " times");
This is somewhat better, but still a bit odd. The reason is that forEach
and peek
can only do their work via side effects. The emerging functional style of Java 8 is to avoid side effects. We did a little of that by extracting the increment of the counter into a count
operation on the stream. Other typical side effects are adding items to collections. Usually these can be replaced via use of collectors. But without knowing what actual work you're trying to do, I can't suggest anything more specific.
As an alternative to sync hassling AtomicInteger one could use an integer array instead. As long as the reference to the array does not get another array assigned (and that's the point) it can be used as a final variable while the values of the fields can change arbitrarily.
int[] iarr = {0}; // final not neccessary here if no other array is assigned
stringList.forEach(item -> {
iarr[0]++;
// iarr = {1}; Error if iarr gets other array assigned
});
AtomicInteger runCount = 0L;
long runCount = myStream.stream()
.filter(...)
.peek(item -> {
foo();
bar();
runCount.incrementAndGet();
});
System.out.println("The lambda ran " + runCount.incrementAndGet() + "times");
You shouldn't use AtomicInteger, you shouldn't use things unless you have a really good reason to use. And the reason for using AtomicInteger might be only allowing concurrent accesses or such as.
When it comes to your problem;
Holder can be use for holding and incrementing it inside a lambda. And after you can get it by calling runCount.value
Holder<Integer> runCount = new Holder<>(0);
myStream.stream()
.filter(...)
.forEach(item -> {
foo();
bar();
runCount.value++; // now it's work fine!
});
System.out.println("The lambda ran " + runCount + " times");
For me, this did the trick, hopefully someone finds it useful:
AtomicInteger runCount = new AtomicInteger(0);
myStream.stream().filter(...).forEach(item -> runCount.getAndIncrement());
System.out.println("The lambda ran " + runCount.get() + "times");
getAndIncrement()
Java documentation states :
Atomically increments the current value, with memory effects as specified by VarHandle.getAndAdd. Equivalent to getAndAdd(1).
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