[Java] Lazy Evaluation (지연 연산)

조민서·2022년 12월 28일
2

JAVA

목록 보기
11/16
post-thumbnail

Lazy Evaluation란?

Lazy Evaluation는 불필요한 연산을 피하기 위해 연산을 지연시켜 놓았다가 필요할 때 연산하는 방법이다.


다음 대화를 보자,

아들: 엄마 나 졸린데, 잘려면 침대랑 베개가 필요해.
엄마: 2개의 방중에 어떤방에 침대랑 베개가 있는지 모르겠네? 엄마는 바쁘니까 아들이 한번 찾아봐
아들: 알겠어.

아들은 잠을 자기위해 2개의 방 중에 침대와 베개 둘다 있는 방을 찾아야 잠을 잘 수 있다.

Java 7 이전

Java 7 이전 Eager Evaluation 구현

static String sleep() {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "sleep";
}

static void print(boolean bed , boolean pillow, String sleep) {
    if(bed && pillow) {
        System.out.println(sleep);
    } else {
        System.out.println("don't sleep");
    }
}

public static void main(String[] args) {
    long startTime = System.currentTimeMillis();

    print(true, true, sleep()); // room1
    print(true, false, sleep()); // room2

    System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}

/*
출력
sleep
don't sleep
2초
*/

위 코드를 보면 room2bed: true지만 pillow: false라는 사실을 우리는 알고있다. 하지만 그거와 별개로 sleep()는 항상 실행하기 때문에 2초라는 시간이 걸린다.
아이는 잠을 자놓고 안 잔척한다.

메모리와 시간을 낭비한다.


Java 8 이후

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

Java 8에서는 다양한 Funtional 함수들이 추가되었다. 그중에 Supplier라는 함수가 있는데,
보이는 거처럼 파라미터를 하나도 받지 않고 T라는 객체를 생성하는 함수형 인터페이스다.
Supplier는 Lazy Evaluation을 지원한다.

Java 8 이후 Lazy Evaluation 구현

static String sleep() {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "sleep";
}

static void print(boolean bed, boolean pillow, Supplier<String> sleep) {
    if(bed && pillow) {
        System.out.println(sleep.get());
    } else {
        System.out.println("don't sleep");
    }
}

public static void main(String[] args) {
    long startTime = System.currentTimeMillis();

    print(true,true, () -> sleep()); // room1
    print(true,false, () -> sleep()); // room2

    System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}

/*
출력
sleep
don't sleep
1초
*/

아이는 불필요한 연산(room2)에서는 잠을 자지 않는다.


그럼 Java 7 이전에는 무조건 메모리와 시간을 버렸나?

Java 7 이전에도 Lazy Evaluation을 구현 할 수는 있었다. 하지만 문제점이 존재했다.

Java 7 이전 Eager Evaluation

static boolean returnTrue() {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return true;
}

static boolean returnFalse() {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return false;
}

public static void main(String[] args) {
    long startTime = System.currentTimeMillis();

    boolean bed = returnTrue();
    boolean pillow = returnFalse();

    if(pillow && bed) {
        System.out.println("잘 수 있다.");
    } else {
        System.out.println("잘 수 없다.");
    }

    System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}
/*
출력
잘 수 없다.
2초
*/

위의 코드를 개선해보자.

Java 7 이전 Lazy Evaluation

public static void main(String[] args) {
    long startTime = System.currentTimeMillis();

    if(returnFalse() && returnTrue()) {
        System.out.println("잘 수 있다.");
    } else {
        System.out.println("잘 수 없다.");
    }

    System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}

/*
출력
잘 수 없다.
1초
*/

위 코드처럼 개선할 수 있다.
returnFalae()에서 false를 리턴하므로 뒤는 볼거도 없이 else문을 출력한다. 즉 Java 자체에서 지연 연산을 지원한다.
하지만 함수의 리턴 값을 변수에 초기화 할 수 없다. 이 문제점을 해결하기 위해 Java 8에 Supplier 함수형 인터페이스가 도입됐다.

supplier.get()

참고

자바 Optional 메서드에서 orElse와 orElseGet의 차이점을 살펴보자.

orElse(함수A()) orElse는 함수A()를 무조건 실행하지만,
orElseGet(함수A()) orElseGet는 함수A()를 lazy 연산 하기 때문에 연산이 필요한 경우에만 동작한다.

profile
내 두뇌는 휘발성 메모리다. 😪

0개의 댓글