티스토리 뷰

● 도입

- 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의 System.out.println("Returning 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;
	}
}

returnFlase method의 System.out.println("Returning 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;
	}
}

returnFlase method의 System.out.println("Returning 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)

lazyOr method를 '&&'으로 바꿨더니 returnFlase method의 System.out.println("Returning false")도 실행된 것을 볼 수 있다.

 

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) 참고하면 좋은 글

https://goodgid.github.io/Java-8-Stream-Debug-Peek/

https://www.tcpschool.com/java/java_stream_intermediate

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함