컬렉션 프레임워크와 Stream API - Stream API 기초

urur-27·2025년 2월 19일

StreamAPI

목록 보기
3/6

Stream API는 컬렉션이나 배열과 같은 다양한 데이터 소스를 연속된 요소(스트림)로 변환하여, 간결하고 효율적으로 데이터를 처리할 수 있도록 돕는 기능

1. Stream API의 개념

Stream?
"데이터가 연속적으로 흘러가는 흐름"을 포괄적으로 가리키며, 적용 분야에 따라 의미가 조금씩 달라진다. Java Stream API는 컬렉션의 요소를 마치 한 줄로 이어진 흐름처럼 다루면서, 중간 연산(filter, map)과 최종 연산(forEach, collect)을 통해 선언적으로 처리할 수 있도록 설계되었다. 이처럼 여러 영역에서 비슷하지만 조금씩 다른 맥락으로 사용되므로, “stream”이라는 단어가 가리키는 대상이 무엇인지 사전에 명확히 구분해야 커뮤니케이션 미스를 줄일 수 있다.

1.1 스트림 파이프라인

  • 데이터 소스: 리스트, 배열, 파일, 생성 함수 등 다양한 소스에서 스트림을 생성할 수 있다.
  • 중간 연산: filter(), map(), sorted(), distinct() 등.
    스트림을 리턴하고, 여러 번 연이어 호출할 수 있다.
  • 최종 연산: forEach(), collect(), reduce() 등.
    스트림을 소모하고 결과를 생성하며, 한 번만 호출할 수 있다.
  • 한 번 최종 연산으로 소비된 스트림 파이프라인은 재사용할 수 없다.
  • 스트림을 위해 사용된 원본 컬렉션 객체는 스트림 연산 후 아무 변경이 일어나지 않아야 한다.
		// 스트림 파이프라인의 예시
        scores.entrySet().stream()
              .filter(highScore) // 중간 연산
              .forEach(entry ->
                  System.out.println("High scorer: " + entry.getKey() + " => " + entry.getValue())); // 최종 연산

1.2 중간 연산과 최종 연산

  • 중간 연산(Intermediate Operation)
    • 여러 번 체이닝 가능
    • 결과는 여전히 스트림이므로, 이후 추가 연산을 계속 적용할 수 있다
  • 최종 연산(Terminal Operation)
    • 연산이 실행되면 스트림을 소모하므로, 더 이상 추가 연산 불가
    • 결과가 Listlong과 같은 실제 값 또는 컬렉션으로 도출된다

1.3 스트림의 지연 평가

  • 실제로 중간 연산이 수행되는 시점은 최종 연산이 호출될 때이다.
  • 이 덕분에 필요 없는 연산은 건너뛰고, 전체 파이프라인을 한 번에 처리할 수 있어 효율적이다.

2. Stream API 기본 연산

익명 클래스와 람다 표현식으로 기본 연산을 이용할 수 있다. 익명 클래스와 람다는 모두 함수형 인터페이스(Predicate, Function, Consumer 등)를 구현하는 방법이지만, 람다 표현식을 사용하면 코드를 훨씬 간결하게 작성할 수 있다.

2.1 filter()와 Predicate

  • filter() : 스트림에 들어있는 각 요소에 대해 조건을 검사한 뒤, 조건을 만족하는 요소만 남기는 중간 연산.
  • 조건 로직은 Predicate 함수형 인터페이스(인자를 받아 true 또는 false를 반환)에 의해 결된다.
    • Predicate은 test(T t) 메서드를 구현하며, true면 요소를 유지, false면 제거한다.

2.2 map()와 Function

  • map(): 스트림의 각 요소를 다른 타입 혹은 다른 값으로 변환하는 중간 연산이다.
  • 변환 로직은 Function 함수형 인터페이스(인자 1개, 변환 후 반환값 1개)로 작성한다.
    • Function은 apply(T t) 메서드를 통해 입력값을 가공하여 반환한다.

2.3 forEach()와 Consumer

  • forEach(): 스트림의 각 요소에 대해 특정 연산(Consumer)을 수행하는 최종 연산이다.
  • 로직은 Consumer 함수형 인터페이스(인자 1개, 반환값 없음)로 작성한다.
    • Consumer은 accept(T t) 메서드를 통해 요소를 소비(처리)한다.
profile
끄아악

0개의 댓글