이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › Java › 람다와 스트림
스트림 지연 평가
스트림은 적힌 순서대로 실행되지 않는다
filter와 map 같은 중간 연산은 호출한 순간에는 아무 일도 하지 않고, 최종 연산이 호출될 때 비로소 실행됩니다. 그것도 연산별로 한꺼번에가 아니라 원소 하나씩 끝까지 흘려보내는 방식입니다.
실행으로 대조 · JDK 21에서 12가지 조합을 실행해 대조
이 실습에 나오는 개념
- Stream API
- 컬렉션 등의 데이터에 대해 필터링, 변환, 집계를 선언적으로 표현하는 API다. filter, map 같은 중간 연산을 이어 붙이고 collect, forEach 같은 최종 연산으로 마무리한다. 중간 연산은 최종 연산이 호출될 때까지 실행되지 않으며(지연 평가), 스트림은 원본 데이터를 변경하지 않고 한 번만 사용할 수 있다.
- 지연 평가(Lazy Evaluation)
- 결과가 실제로 필요해질 때까지 계산을 미루는 방식이다. 스트림의 중간 연산은 호출 즉시 실행되지 않고 최종 연산이 호출될 때 비로소 실행되고 원소가 하나씩 파이프라인을 지나가며, 필요한 만큼만 처리하고 멈출 수 있다. JPA의 지연 로딩도 같은 발상으로, 연관 데이터를 실제로 사용할 때 조회한다.
- 람다 표현식
- 이름 없는 함수를 간결하게 표현하는 문법으로 Java 8에서 도입되었다. (매개변수) -> 실행문 형태로 쓰며, 추상 메서드가 하나뿐인 함수형 인터페이스의 구현으로 사용된다. 동작(코드)을 값처럼 다른 메서드에 전달할 수 있게 해 준다.
- Optional
- 값이 있을 수도 없을 수도 있음을 타입으로 표현하는 컨테이너 클래스다. 메서드가 null을 반환하는 대신 Optional을 반환하면 호출하는 쪽이 값이 없는 경우를 처리하도록 유도할 수 있다. 주로 메서드의 반환 타입으로 사용한다.
이렇게 바꿔 보세요
- 처음 설정으로 — 원소 하나가 filter와 map을 끝까지 지난 뒤 다음 원소로 넘어갑니다.
- 최종 연산을 빼면 — filter도 map도 한 번도 실행되지 않습니다.
- findFirst() — 첫 원소를 찾는 순간 멈춥니다. 나머지는 보지도 않습니다.
- limit(2) — 두 개가 채워지면 남은 원소는 처리하지 않습니다.
- sorted()를 넣으면 — 정렬하려면 전부 모아야 하므로 filter가 끝까지 먼저 돕니다.
처음 설정으로 실행하면
- stream(), filter(), map()을 차례로 호출했습니다. 이 호출들은 "무엇을 할지"를 연결해 둘 뿐, 원소를 하나도 꺼내지 않습니다. 람다는 아직 한 번도 실행되지 않았습니다.
- toList()가 호출됐습니다. 이제 실행이 시작됩니다. 최종 연산이 원소를 하나씩 끌어당깁니다.
- filter(5): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
- map(5): 50으로 바꿔서 결과에 담습니다.
- filter(2): 3보다 크지 않습니다. 버립니다. 이 원소는 map에 가지 않습니다.
- filter(8): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
- map(8): 80으로 바꿔서 결과에 담습니다.
- filter(1): 3보다 크지 않습니다. 버립니다. 이 원소는 map에 가지 않습니다.
- filter(9): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
- map(9): 90으로 바꿔서 결과에 담습니다.
- filter(4): 3보다 큽니다. 통과. 곧바로 다음 연산인 map으로 넘어갑니다.
- map(4): 40으로 바꿔서 결과에 담습니다.
- 결과: [50, 80, 90, 40]
실행 결과
- "filter를 전부 한 다음 map을 전부"가 아니라, 원소 하나가 filter → map을 끝까지 통과한 뒤 다음 원소가 출발합니다. 그래서 중간에 멈출 수 있습니다.
코드
List.of(5, 2, 8, 1, 9, 4).stream()
.filter(x -> x > 3) // 3보다 큰 것만
.map(x -> x * 10) // 10배
.toList();
이 모형의 전제
12가지 조합 모두의 실행 순서와 결과를 이 PC의 JDK 21에서 같은 파이프라인을 실행한 결과와 대조했습니다. 순차 스트림 기준이며, 병렬 스트림에서는 원소가 처리되는 순서가 달라집니다.
앞 실습: 람다와 함수형 인터페이스 · 다음 실습: 스트림으로 모으기