-
Java Lazy Evaluation이란?Java 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.019Java 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
https://www.youtube.com/watch?v=7e7FCMFrwcg&list=PLRIMoAKN8c6O8_VHOyBOhzBCeN7ShyJ27&index=7
https://sabarada.tistory.com/154?category=815130
'Java' 카테고리의 다른 글
[Java] 날짜,시간과 관련된 LocalDateTime의 역사 (0) 2022.08.27 [Java]ExecutorService란? (0) 2022.07.31 책임 연쇄 패턴이란? (0) 2022.07.19 불변객체란 무엇인가? (0) 2022.05.29 Java에 원시타입(primitive type)이 존재하는 이유 (0) 2022.05.28