Java

Java Lazy Evaluation이란?

Junuuu 2022. 7. 24. 00:01
반응형

Lazy Evaluation이란?

실제로 필요한 경우에만 연산을 시작하는 것을 의미합니다.

 

반대로 eager evaluation은 할당되자마자 연산을 시작합니다.

 

Java는 eager evaluation을 기본으로 하고 일부 Lazy Evaluation이 존재했습니다.

 

Java8이 나오면서 Java에서 Lazy Evauation을 좀 더 유연하게 사용할 수 있습니다.

 

 

Eagar Evaluation 예시

public class LazyEvaluation {
    public static void main(String[] args) throws InterruptedException {

        double start = System.currentTimeMillis();
        printIfValid(0,expensiveMethod("junuuu"));
        printIfValid(-1,expensiveMethod("junuuu"));
        printIfValid(-2,expensiveMethod("junuuu"));

        System.out.println("소요 시간 : " + (System.currentTimeMillis() - start) / 1000);

    }

    private static void printIfValid(int validValue, String printValue){
        if(validValue >= 0){
            System.out.println("The value is " + printValue);
        } else{
            System.out.println("Invalid");
        }

    }

    private static String expensiveMethod(String value) throws InterruptedException {
        Thread.sleep(3000);
        return value;
    }
}

위의 코드는 printIfValid 메서드가 3번 호출되고 이에 대한 소요시간을 측정하는 코드입니다.

 

printIfValid 메서드는 validValue, printValue를 인자로 받고 validValue의 값에 따라 두 번째 인자인 printValue를 사용하기도 하고 사용하지 않기도 합니다.

 

만약 -1, -2가 첫 번째 인자로 들어왔다면 printValue는 필요하지 않을 것이고 expensiveMethod가 호출되지 않는다면 훨씬 좋을 것입니다.

 

하지만 위의 결과는 다음과 같이 출력됩니다.

The value is junuuu
Invalid
Invalid
소요 시간 : 9.019

 

Java 7 이전의 Lazy Evaluation

조건문에 직접 명시하는 방법밖에 존재하지 않습니다.

이렇게 하면 결괏값을 변수로 받은 후 재활용이 안 되는 단점이 있습니다.

 

예를 들어 printIfValid 메서드가 다음과 같이 변해야 합니다.

private static void printIfValid(int validValue){
        if(validValue >= 0){
            System.out.println("The value is " + expensiveMethod("junuuu"));
        } else{
            System.out.println("Invalid");
        }

    }

 

이렇게 되면 else 구문에서는 expensiveMethod가 호출되지 않지만 변수로서 재활용도는 떨어지게 됩니다.

 

Java 8 이후의 Lazy Evaluation

import java.util.function.Supplier;

public class LazyEvaluation {
    public static void main(String[] args) {

        double start = System.currentTimeMillis();
        Supplier<String> printValueSupplier = new Supplier<String>() {
            @Override
            public String get() {
                return expensiveMethod("junuuu");
            }
        };

        printIfValid(0, printValueSupplier);
        printIfValid(-1, () -> expensiveMethod("junuuu"));
        printIfValid(-2, () -> expensiveMethod("kim"));

        System.out.println("소요 시간 : " + (System.currentTimeMillis() - start) / 1000);

    }

    private static void printIfValid(int validValue, Supplier<String> printValueSupplier) {
        if (validValue >= 0) {
            System.out.println("The value is " + printValueSupplier.get());
        } else {
            System.out.println("Invalid");
        }

    }

    private static String expensiveMethod(String value) {

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return value;
    }
}

 

다양한 Funtional Interface들이 등장하게 되었고 그중 Supplier라는 함수가 있습니다.

 

Supplier는 이름을 통해서 유추할 수 있듯이 파라미터를 하나도 받지 않고 T라는 객체를 생성하는 함수입니다.

 

Supplier는 get 메서드를 통하여 값을 반환할 수 있습니다.

 

람다식이 익숙하지 않은 분들이 있을 수 있을 것 같아 익명 클래스로 한 번 표현하고 람다식으로 2번 표현해 보았습니다.

 

원래라면 바로 호출하던 것을 함수화시켜서 실제로 validValue가 0이 넘을 때만 get 메서드를 호출하면서 Lazy Evalutaion이 구현 가능해집니다.

 

Java 8 Stream과 Lazy Evaluation

Java 8 Stream에서는 Lazy Evaluation이 기본이 됩니다.

각 단계에 모든 원소에 대해서 연산을 하지 않고 필요한 부분만 연산을 처리합니다.

 

만약 List에 1~15까지의 수가 존재하고 여기서 처음 3개의 값을 뽑아내는 연산을 구현해 보겠습니다.

import java.util.ArrayList;
import java.util.List;

public class StreamLazyEvaluation {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);

        int count = 0;
        List<Integer> result = new ArrayList<>();
        for(Integer cur : numbers){
            System.out.println("curNum : " + cur);
            if(count++ < 3 ){
                result.add(cur);
            }
        }
        System.out.println(result);
    }
}

 

위의 코드로 결과로는 다음과 같이 15번의 curNum이 출력됩니다.

curNum : 1
curNum : 2
curNum : 3
curNum : 4
curNum : 5
curNum : 6
curNum : 7
curNum : 8
curNum : 9
curNum : 10
curNum : 11
curNum : 12
curNum : 13
curNum : 14
curNum : 15
[1, 2, 3]

 

즉, Eager Evaluation입니다. 여기서 Stream을 도입해 보겠습니다.

 

Stream으로 1~15까지에서 처음 3개 수 뽑아내기

import java.util.List;
import java.util.stream.Collectors;

public class StreamLazyEvaluation {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);

        List<Integer> result = numbers.stream()
                                      .peek(cur -> System.out.println("curNum : " + cur))
                                      .limit(3)
                                      .collect(Collectors.toList());
        System.out.println(result);
    }
}

1. List를 stream으로 변환합니다.
2. 중간중간 현재 수를 peek를 사용하여 출력합니다.

3. limit메서드로 결과를 최대 3개로 제한합니다.

4. collect메서드로 결과를 List로 바꿔줍니다.

 

위의 코드의 결과는 다음과 같습니다.

curNum : 1
curNum : 2
curNum : 3
[1, 2, 3]

 

코드도 엄청나게 간결해졌으며, Lazy Evaluation이 적용되었습니다.

 

 

출처

https://sabarada.tistory.com/153

 

[Java] Java와 Lazy Evaluation

Lazy evaluation은 실제로 필요로 해지는 경우에 연산을 시작하는 것입니다. 이 반대로는 eager evaluation이 있으며 이는 할당되자마다 연산을 시작합니다. 기본적인 Java의 기조는 eager evaluation을 기본으

sabarada.tistory.com

https://www.youtube.com/watch?v=7e7FCMFrwcg&list=PLRIMoAKN8c6O8_VHOyBOhzBCeN7ShyJ27&index=7 

https://sabarada.tistory.com/154?category=815130 

 

[java] Java와 Lazy evaluation - Java8 Stream

안녕하세요. 이전시간에 우리는 Lazy evaluation에 대해서 알아보는 시간을 가졌었습니다. 오늘은 이어서 Java8의 Stream의 Lazy Evaluation에 대해서 알아보는 시간을 가져보겠습니다. 우리는 이전 시간 [Ja

sabarada.tistory.com