개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › Java › 람다와 스트림

스트림 지연 평가

스트림은 적힌 순서대로 실행되지 않는다

filter와 map 같은 중간 연산은 호출한 순간에는 아무 일도 하지 않고, 최종 연산이 호출될 때 비로소 실행됩니다. 그것도 연산별로 한꺼번에가 아니라 원소 하나씩 끝까지 흘려보내는 방식입니다.

실행으로 대조 · JDK 21에서 12가지 조합을 실행해 대조

이 실습에 나오는 개념

Stream API
컬렉션 등의 데이터에 대해 필터링, 변환, 집계를 선언적으로 표현하는 API다. filter, map 같은 중간 연산을 이어 붙이고 collect, forEach 같은 최종 연산으로 마무리한다. 중간 연산은 최종 연산이 호출될 때까지 실행되지 않으며(지연 평가), 스트림은 원본 데이터를 변경하지 않고 한 번만 사용할 수 있다.
지연 평가(Lazy Evaluation)
결과가 실제로 필요해질 때까지 계산을 미루는 방식이다. 스트림의 중간 연산은 호출 즉시 실행되지 않고 최종 연산이 호출될 때 비로소 실행되고 원소가 하나씩 파이프라인을 지나가며, 필요한 만큼만 처리하고 멈출 수 있다. JPA의 지연 로딩도 같은 발상으로, 연관 데이터를 실제로 사용할 때 조회한다.
람다 표현식
이름 없는 함수를 간결하게 표현하는 문법으로 Java 8에서 도입되었다. (매개변수) -> 실행문 형태로 쓰며, 추상 메서드가 하나뿐인 함수형 인터페이스의 구현으로 사용된다. 동작(코드)을 값처럼 다른 메서드에 전달할 수 있게 해 준다.
Optional
값이 있을 수도 없을 수도 있음을 타입으로 표현하는 컨테이너 클래스다. 메서드가 null을 반환하는 대신 Optional을 반환하면 호출하는 쪽이 값이 없는 경우를 처리하도록 유도할 수 있다. 주로 메서드의 반환 타입으로 사용한다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. stream(), filter(), map()을 차례로 호출했습니다. 이 호출들은 "무엇을 할지"를 연결해 둘 뿐, 원소를 하나도 꺼내지 않습니다. 람다는 아직 한 번도 실행되지 않았습니다.
  2. toList()가 호출됐습니다. 이제 실행이 시작됩니다. 최종 연산이 원소를 하나씩 끌어당깁니다.
  3. filter(5): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
  4. map(5): 50으로 바꿔서 결과에 담습니다.
  5. filter(2): 3보다 크지 않습니다. 버립니다. 이 원소는 map에 가지 않습니다.
  6. filter(8): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
  7. map(8): 80으로 바꿔서 결과에 담습니다.
  8. filter(1): 3보다 크지 않습니다. 버립니다. 이 원소는 map에 가지 않습니다.
  9. filter(9): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
  10. map(9): 90으로 바꿔서 결과에 담습니다.
  11. filter(4): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
  12. map(4): 40으로 바꿔서 결과에 담습니다.
  13. 결과: [50, 80, 90, 40]

실행 결과

코드

List.of(5, 2, 8, 1, 9, 4).stream()
    .filter(x -> x > 3)          // 3보다 큰 것만
    .map(x -> x * 10)            // 10배
    .toList();

이 모형의 전제

12가지 조합 모두의 실행 순서와 결과를 이 PC의 JDK 21에서 같은 파이프라인을 실행한 결과와 대조했습니다. 순차 스트림 기준이며, 병렬 스트림에서는 원소가 처리되는 순서가 달라집니다.

앞 실습: 람다와 함수형 인터페이스 · 다음 실습: 스트림으로 모으기

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요