티스토리 뷰
● 도입
- Lazy Evaluation : 말 그대로 계산을 게으르게 하는 것이다. 계산값이 필요할 때까지 미루고 미루다가 꼭 필요해지면 그때가서 계산을 하는 것이다.(Lambda의 계산은 그 결과값이 필요할 때가 되어서야 계산된다.)
- 이를 이용하여 불필요한 계산을 줄이거나 해당 코드의 실행 순서를 의도적으로 미룰 수 있다.
cf) Stream은 Lazy Evaluation을 한다.
● 실습
1.
- if문 안의 조건들을 or(||)로 엮을 때 아래와 같은 경우라면 returnFlase method는 실행조차 되지 않는다.
- 이미 전체 조건이 true로 결정났기 때문에 뒤에 false가 오든 true가 오든 상관없이 무조건 true가 되기 때문이다.
package com.fastcampus.functionalprogramming.chapter9;
public class Chapter9Section2 {
public static void main(String[] args) {
if(true || returnFlase()) { // returnFlase method는 실행조차 되지 않는다.
System.out.println("true");
}
}
public static boolean returnTrue() {
System.out.println("Returning true");
return true;
}
public static boolean returnFlase() {
System.out.println("Returning false");
return false;
}
}
- 아래의 코드에서도 동일하게 returnFlase method는 실행조차 되지 않는다. returnTrue method만 실행된다.
package com.fastcampus.functionalprogramming.chapter9;
public class Chapter9Section2 {
public static void main(String[] args) {
if(returnTrue() || returnFlase()) { // returnFlase method는 실행조차 되지 않는다.
System.out.println("true");
}
}
public static boolean returnTrue() {
System.out.println("Returning true");
return true;
}
public static boolean returnFlase() {
System.out.println("Returning false");
return false;
}
}
cf) && 조건도 동일하게 전체가 true 여야하는 상황에서 하나만 false가 있어도 뒤쪽은 실행하지 않는다.
(어차피 false가 되니까)
2.
- 1번의 경우를 '최적화' 되어있다고 한다.
- 하지만 아래와 같은 코드라면 returnTrue method와 returnFlase method가 모두 실행된다.
package com.fastcampus.functionalprogramming.chapter9;
public class Chapter9Section2 {
public static void main(String[] args) {
if(or(returnTrue(), returnFlase())) {
System.out.println("true_all");
}
}
public static boolean or(boolean x, boolean y) {
return x || y;
}
public static boolean returnTrue() {
System.out.println("Returning true");
return true;
}
public static boolean returnFlase() {
System.out.println("Returning false");
return false;
}
}
- returnTrue method와 returnFlase method가 모두 계산이 된 후에야 true_all이 출력된다.
- 즉, 1번과 달리 최적화가 이루어지지 않았다는 것이다.
[최적화가 이루어지지 않은 이유]
- main method에서 or method를 호출하기 전에 argument로 주어진 (returnTrue(), returnFlase())의 값들을 모두 알고 난 후에야 or method를 호출하기 때문이다.
- 만약 returnTrue method와 returnFlase method가 계산이 오래걸리고 무거운 작업이라면 런타임이 상당히 지연될 것이다.
- 최적화를 통해 계산을 최대한 줄일 수 있다면 이러한 문제가 해결될 것이다.
- 바로 이때 Lazy Evaluation을 사용하는 것이다.
3. Lazy Evaluation
@FunctionalInterface
public interface Supplier<T> {
T get();
}
package com.fastcampus.functionalprogramming.chapter9;
import java.util.function.Supplier;
public class Chapter9Section2 {
public static void main(String[] args) {
// Supplier를 거쳐서 값을 받아오도록 했다.
if(lazyOr(() -> returnTrue(), () -> returnFlase())) {
System.out.println("true_all_two");
}
}
public static boolean lazyOr(Supplier<Boolean> x, Supplier<Boolean> y) {
return x.get() || y.get();
}
public static boolean returnTrue() {
System.out.println("Returning true");
return true;
}
public static boolean returnFlase() {
System.out.println("Returning false");
return false;
}
}
- 2번의 경우와 거의 동일한 모양의 코드이지만 () -> returnFlase() 가 실행이 되지 않은 것을 확인할 수 있다.
- (() -> returnTrue(), () -> returnFlase())가 lazyOr method에 Supplier 형태로 넘어간 후
- x.get()을 먼저 실행했을 때 값이 true 인 것을 알았기 때문에 '||' 조건으로 인해 뒤의 y.get()은 계산을 하지 않은 것이다.
(뒤의 값이 무엇이든 상관 없이 어차피 결과는 true 이니까)
[차이점]
- 2번의 경우 or method 호출 전에 (returnTrue(), returnFlase())를 '모두 계산한 후' 그 결과값을
or method에 전달했다.
- 3번은 lazyOr method에 (() -> returnTrue(), () -> returnFlase())를 '넘겨준 것'이다.
- 그 후 lazyOr method 안에서 x.get()을 계산한 후 해당 결과값이 true로 나왔기 때문에 뒤의 y.get()은
계산을 하지도 않고 그냥 넘어간 것이다.('||' 조건으로 인해 어차피 최종 결과값이 true인 것을 아니까)
cf)
4. Stream은 Lazy Evaluation을 한다.
Stream<Integer> integerStream = Stream.of(3, -2, 5, 8, -3, 10)
.filter(x -> x > 0)
.peek(x -> System.out.println("peeking " + x))
.filter(x -> x % 2 == 0);
System.out.println("Before collect");
List<Integer> integers = integerStream.collect(Collectors.toList());
System.out.println("After collect: " + integers);
- 단순히 위 코드만 보면 아래와 같은 식으로 log가 찍혔으리라 생각할 수 있다.
peeking 3
peeking 5
peeking 8
peeking 10
Before collect
After collect: [8, 10]
- 하지만 실제 log가 찍힌 모습은 아래와 같다.
Before collect
peeking 3
peeking 5
peeking 8
peeking 10
After collect: [8, 10]
왜 이럴까?
- Stream은 종결 처리가 이루어지기 전까지는 모든 계산을 미루기 때문에 "Before collect"가 먼저 출력됐다.
- Stream이 계산을 미루고 미루다가 종결 처리를 만나고 나서야 '이 값들을 계산할 필요가 있구나' 하고 그제서야
한꺼번에 계산을 하기 시작하는 것이다.
- 그렇기에 Before collect가 먼저 출력되고 collect(Collectors.toList())를 하기 전에 peeking을 한 후 마지막에 After collect가 출력되는 것이다.
cf) 위 코드에서 종결처리 = 'collect(Collectors.toList())'
package com.fastcampus.functionalprogramming.chapter9;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Chapter9Section2 {
public static void main(String[] args) {
Stream<Integer> integerStream = Stream.of(3, -2, 5, 8, -3, 10)
.filter(x -> x > 0)
.peek(x -> System.out.println("peeking " + x))
.filter(x -> x % 2 == 0);
System.out.println("Before collect");
List<Integer> integers = integerStream.collect(Collectors.toList());
System.out.println("After collect: " + integers);
/*
* peek
* - Consumer를 받아서 단순하게 Consumer를 실행만 하고 그대로 넘어가는 Stream method
* - 잠깐 힐긋 보는 느낌의 method이다.
* - 어차피 peek method 배우는 글이 아니니까 지금은 이 정도로만 알자
*/
/*
* - Stream은 종결 처리가 이루어지기 전까지 모든 계산을 미루기 때문에 "Before collect"가 먼저 출력됐다.
*
* 자세히
* - Stream이 계산을 미루고 미루다가 종결 처리를 만나고 나서야 '이 값들을 계산할 필요가 있구나' 하고 그제서야
* 한꺼번에 계산을 하기 시작한다.
* - 위 코드에서 종결처리 = 'collect(Collectors.toList())'
*/
}
}
cf) 참고하면 좋은 글
'Backend > Java8' 카테고리의 다른 글
#37 Design Pattern - Builder Pattern (0) | 2022.10.26 |
---|---|
#36 Design Pattern (0) | 2022.10.25 |
#34 함수형 프로그래밍 응용 - Scope & Closure / Curry (0) | 2022.10.23 |
#33 Advanced Stream - Parallel Stream(병렬 Stream) (0) | 2022.10.22 |
#32 Advanced Stream - forEach (0) | 2022.10.21 |
- Total
- Today
- Yesterday
- 프로세스
- MySQL
- jpa
- 코딩테스트
- node.js
- 자료구조
- Stream
- DART
- 알고리즘
- OS
- 프로그래머스
- 코테
- git
- Spring Boot
- nosql
- Advanced Stream
- Phaser
- SQL
- SpringBoot
- 메모리
- db
- Java8
- 빅데이터 분석기사
- java
- spring
- API
- 빅데이터
- 운영체제
- Phaser3
- MongoDB
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |