Java 8: preferred way to count iterations of a lambda?

后端 未结 11 1724
没有蜡笔的小新
没有蜡笔的小新 2021-02-01 00:17

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         


        
相关标签:
11条回答
  • 2021-02-01 00:32

    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.

    0 讨论(0)
  • 2021-02-01 00:33

    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
        });
    
    0 讨论(0)
  • 2021-02-01 00:34

    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.

    0 讨论(0)
  • 2021-02-01 00:36

    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");
    
    0 讨论(0)
  • 2021-02-01 00:38

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

    0 讨论(0)
  • 2021-02-01 00:39
    AtomicInteger runCount = 0L;
    long runCount = myStream.stream()
        .filter(...)
        .peek(item -> { 
            foo();
            bar();
            runCount.incrementAndGet();
        });
    System.out.println("The lambda ran " + runCount.incrementAndGet() + "times");
    
    0 讨论(0)
提交回复
热议问题