programing

Java 8: 람다의 반복 횟수를 세는 데 선호되는 방법?

newsource 2022. 9. 22. 00:25

Java 8: 람다의 반복 횟수를 세는 데 선호되는 방법?

나는 자주 같은 문제에 직면한다.람다 밖에서 사용하기 위해 람다의 런을 세어야 합니다.

예:

myStream.stream().filter(...).forEach(item -> { ... ; runCount++});
System.out.println("The lambda ran " + runCount + "times");

문제는 runCount가 다음과 같이 되어 있어야 한다는 것입니다.final 「 」, 「 」, 「 」는 수.int은...일 수 .Integer그건 불변의 일이니까클래스 레벨 변수(필드)로 만들 수 있지만 이 코드 블록에서만 필요합니다.

여러 가지 방법이 있다는 것을 알고 있는데, 이 문제에 대해 어떤 해결책을 선호하는지 궁금할 뿐입니다.
를하 an an an an an an an를 합니까?AtomicInteger또는 어레이 레퍼런스나 다른 방법으로요?

설명을 위해 예를 조금 다시 포맷하겠습니다.

long runCount = 0L;
myStream.stream()
    .filter(...)
    .forEach(item -> { 
        foo();
        bar();
        runCount++; // doesn't work
    });
System.out.println("The lambda ran " + runCount + " times");

람다 내에서 카운터를 증가시켜야 하는 경우 일반적인 방법은 카운터를AtomicInteger ★★★★★★★★★★★★★★★★★」AtomicLong그 후, 인크리먼트 메서드 중 하나를 호출합니다.

엘리먼트를 할 수 .int ★★★★★★★★★★★★★★★★★」long어레이는 스트림이 병렬로 실행되면 레이스 조건이 됩니다.

, 은 「」으로 끝나는 해 주세요.forEach아, 아, 아, 아, 아, 아, 아, 맞다. 바꿀 수 있어요.forEach a까지peek이치노

long runCount = myStream.stream()
    .filter(...)
    .peek(item -> { 
        foo();
        bar();
    })
    .count();
System.out.println("The lambda ran " + runCount + " times");

이이좀 、 ,,,,, 、 래래해해해해 。 는 ★★★★★★★★★★★★★★★★★★★.forEach ★★★★★★★★★★★★★★★★★」peek을 사용하다자바8의 새로운 기능 스타일은 부작용을 피하기 위한 것이다.하여 조금 .count컬렉션에 이 추가되는 가 있습니다.다른 전형적인 부작용은 컬렉션에 아이템을 추가하는 것이다.일반적으로 수집기를 사용하여 교체할 수 있습니다.하지만 실제로 어떤 일을 하려는지 모르면 더 이상 구체적으로 제안할 수 없습니다.

동기 ATOMIC Integer 대신 정수 배열을 사용할 수 있습니다.어레이에 대한 참조가 다른 어레이를 할당받지 않는 한(그것이 포인트) 필드을 임의로 변경할 수 있지만 최종 변수로 사용할 수 있습니다.

    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");

Atomic Integer를 사용하면 됩니다.사용해야 할 이유가 없는 한 물건을 사용하면 안 됩니다.또한 AtomicInteger를 사용하는 이유는 동시접속 등을 허용하기 위해서일 수 있습니다.

고객의 문제에 관한 한

홀더는 람다 내에서 홀더를 고정하고 증가시키는 데 사용할 수 있습니다.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");

저는 이 방법이 효과가 있었습니다.누군가 유용하게 써주셨으면 합니다.

AtomicInteger runCount = new AtomicInteger(0);
myStream.stream().filter(...).forEach(item -> runCount.getAndIncrement());
System.out.println("The lambda ran " + runCount.get() + "times");

getAndIncrement()Java 문서 상태:

VarHandle.getAndAdd에서 지정한 메모리 효과와 함께 현재 값을 아토믹하게 증가시킵니다.getAndAdd(1)와 동일합니다.

또 다른 대안은 Apache Commons Mutable을 사용하는 것입니다.내부.

MutableInt cnt = new MutableInt(0);
myStream.stream()
    .filter(...)
    .forEach(item -> { 
        foo();
        bar();
        cnt.increment();
    });
System.out.println("The lambda ran " + cnt.getValue() + " times");

로컬에만 필요하므로 필드를 만들지 않으려면 익명 클래스에 저장할 수 있습니다.

int runCount = new Object() {
    int runCount = 0;
    {
        myStream.stream()
                .filter(...)
                .peek(x -> runCount++)
                .forEach(...);
    }
}.runCount;

이상해, 알아.단, 일시변수는 로컬 범위에서도 제외됩니다.

다른 방법(작업이 성공한 경우 등 일부 상황에서만 카운트를 증가시키고 싶은 경우 유용함)은 다음과 같습니다.mapToInt()그리고.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");

Stuart Marks가 언급했듯이, 이것은 여전히 약간 이상하다. 왜냐하면 이것은 부작용을 완전히 피하지 않기 때문이다(무엇에 따라 달라짐).foo()그리고.bar()하고 있습니다).

람다에서 외부에서 액세스할 수 있는 변수를 증가시키는 또 다른 방법은 클래스 변수를 사용하는 것입니다.

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++;
        });
    }
}

이 예에서는 하나의 메서드에서 카운터에 클래스 변수를 사용하는 것은 의미가 없을 수 있으므로 특별한 이유가 없는 한 주의합니다.클래스 변수 유지final가능하면 스레드 안전성 등에 도움이 될 수 있습니다(사용방법에 대한 자세한 내용은 http://www.javapractices.com/topic/TopicAction.do?Id=23 참조).final).

람다가 왜 이렇게 작동하는지 더 잘 알기 위해 https://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood에서 자세히 살펴봅니다.

reduce도 효과가 있습니다.이렇게 사용할 수 있습니다.

myStream.stream().filter(...).reduce((item, sum) -> sum += item);

저는 이게 제일 우아한 방법이에요.

long count = list.stream()
  .peek(/* do your stuff here */)
  .long();

JDK 9,10 에는, 상기의 솔루션이 동작하지 않는 버그가 있습니다만, 다음과 같이 대처해 주세요.https://bugs.openjdk.java.net/browse/JDK-8198356

long count = list.stream()
  .peek(/* do your stuff here */)
  .collect(Collectors.counting());
AtomicInteger runCount = new AtomicInteger(0);

elements.stream()
  //...
  .peek(runCount.incrementAndGet())
  .collect(Collectors.toList());

// runCount.get() should have the num of times lambda code was executed

enum사용할 수 있습니다.특히 반복에 여러 카운터가 있는 경우:

import java.util.Arrays;

class LambdaCounter {

    enum CountOf {

        NO,
        OK,
        ERROR;

        private int count;

        // can be named inc(), instead of the Greek capital Delta,
        // which stands for the math increment operator '∆' <https://en.wikipedia.org/wiki/%E2%88%86>
        synchronized int Δ( final int... times ) {

            if ( times.length <= 0 )
                return ++count; // increase by 1

            return count += Arrays.stream( times ).sum(); // increase by arguments
        }

        // can be named val[ue](), instead of the Greek capital Xi,
        // which stands for the math identity operator '≡' <https://en.wikipedia.org/wiki/Triple_bar>
        int Ξ() {
            return count;
        }
    }

    public static void main( final String[] args ) {

        Arrays.stream( new int[] { 1, 2, 3, 4, 5, 6, 7 } )
            .forEach( i -> {
                CountOf.NO.Δ();
                @SuppressWarnings( "unused" )
                final int LHS_DUMMY =
                    i % 2 == 0
                        ? CountOf.OK.Δ()
                        : CountOf.ERROR.Δ();
            } );
        System.out.printf( "No: %d, OK: %d, Error: %d, Error.inc(38): %d, Error.inc(4, 4): %d%n",
            CountOf.NO.Ξ(), CountOf.OK.Ξ(), CountOf.ERROR.Ξ(), CountOf.ERROR.Δ( 38 ), CountOf.ERROR.Δ( 4, 4 ) );

        // Output:
        // No: 7, OK: 3, Error: 4, Error.inc(38): 42, Error.inc(4, 4): 50
    }
}

언급URL : https://stackoverflow.com/questions/28790784/java-8-preferred-way-to-count-iterations-of-a-lambda