I face the same problem often. I need to count the runs of a lambda for use outside the lambda.
E.g.:
myStream.stream().filter(...).forEa
If you don't want to create a field because you only need it locally, you can store it in an anonymous class:
int runCount = new Object() {
int runCount = 0;
{
myStream.stream()
.filter(...)
.peek(x -> runCount++)
.forEach(...);
}
}.runCount;
Weird, I know. But it does keep the temporary variable out of even local scope.
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
});
Another way of doing this (useful if you'd like your count to only be incremented in some cases, like if an operation was successful) is something like this, using mapToInt()
and sum()
:
int count = myStream.stream()
.filter(...)
.mapToInt(item -> {
foo();
if (bar()){
return 1;
} else {
return 0;
})
.sum();
System.out.println("The lambda ran " + count + "times");
As Stuart Marks noted, this is still somewhat odd, because it's not completely avoiding side effects (depending on what foo()
and bar()
are doing).
And another way of incrementing a variable in a lambda that's accessible outside of it is to use a class variable:
public class MyClass {
private int myCount;
// Constructor, other methods here
void myMethod(){
// does something to get myStream
myCount = 0;
myStream.stream()
.filter(...)
.forEach(item->{
foo();
myCount++;
});
}
}
In this example, using a class variable for a counter in one method probably doesn't make sense, so I'd caution against it unless there's a good reason to. Keeping class variables final
if possible can be helpful in terms of thread safety, etc (see http://www.javapractices.com/topic/TopicAction.do?Id=23 for a discussion on using final
).
To get a better idea of why lambdas work the way they do, https://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood has a detailed look.
Another alternative is to use apache commons MutableInt.
MutableInt cnt = new MutableInt(0);
myStream.stream()
.filter(...)
.forEach(item -> {
foo();
bar();
cnt.increment();
});
System.out.println("The lambda ran " + cnt.getValue() + " 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).
AtomicInteger runCount = 0L;
long runCount = myStream.stream()
.filter(...)
.peek(item -> {
foo();
bar();
runCount.incrementAndGet();
});
System.out.println("The lambda ran " + runCount.incrementAndGet() + "times");