← SEARCHABLE ARCHIVE
Java · 2026-08-03

Java 기초부터 마스터까지 — 6화

람다, Stream, Optional로 데이터 흐름 만들기

INTERACTIVE READING MODE →

본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.

컬렉션에서 조건에 맞는 값을 고르고 변환하고 합치는 코드는 자주 등장한다. 람다와 Stream은 데이터가 어떤 단계를 거치는지 연산의 흐름으로 표현한다.

람다

Predicate<Monster> isAlive = monster -> monster.getHealth() > 0;
Function<Monster, String> getName = Monster::getName;

람다는 동작을 값처럼 전달할 수 있게 한다. 메서드 참조는 이미 존재하는 메서드를 같은 함수 형태로 사용할 때 간결하다.

Stream 파이프라인

List<String> targetNames = monsters.stream()
    .filter(Monster::isAlive)
    .sorted(Comparator.comparingInt(Monster::getHealth))
    .limit(3)
    .map(Monster::getName)
    .toList();

살아 있는 몬스터를 체력순으로 정렬하고 세 개의 이름을 얻는 흐름이다. 중간 연산은 보통 최종 연산이 호출될 때 실행된다.

Stream을 한 번 소비하면 같은 스트림을 다시 사용할 수 없다. 원본 컬렉션을 반복해서 질의하거나 결과를 목록으로 저장해야 한다.

Optional

Optional<Item> item = repository.find(10);

String name = item
    .map(Item::getName)
    .orElse("Unknown");

Optional은 값이 있을 수도 없을 수도 있다는 사실을 반환 타입에 표시한다. get()을 바로 호출하면 없음 처리를 다시 잃어버리므로 orElse, map, ifPresent 같은 연산을 사용한다.

Optional을 모든 필드와 매개변수에 쓰기보다 반환값의 부재를 표현하는 데 사용하는 경우가 많다.

성능과 가독성

Stream이 항상 반복문보다 빠른 것은 아니며 작은 연산 객체와 결과 목록이 만들어질 수 있다. 읽기 쉬운 흐름으로 시작한 뒤 실제 측정에서 문제가 되는 반복 경로만 단순 루프로 바꾸는 편이 좋다.

람다와 Stream의 목적은 코드를 가장 짧게 만드는 것이 아니다. 필터, 변환, 정렬이라는 데이터 처리 단계를 부작용 없이 읽을 수 있게 만드는 것이다.