1. Question

스트림 API(Stream API)란 무엇인가?

2. Answer

Java의 Stream APIJava 8에서 도입되어 데이터 컬렉션을 함수형 스타일로 처리할 수 있게 하는 기능이다. 이는 데이터를 추상화하고 처리하는 다양한 연산을 수행할 수 있게 해준다. 특히, 데이터 소스에 대한 계산 로직을 고수준에서 간결하게 표현할 수 있어, 코드의 가독성과 유지보수성이 향상된다.

다음은 Stream API의 사용 예시이다.

List<String> myList = Arrays.asList("a1", "a2", "b1", "b2", "c1", "c2");
myList.stream()
  .filter(s -> s.startsWith("c"))
  .map(String::toUpperCase)
  .sorted()
  .forEach(System.out::println);

3. Detail

A. 스트림이란?

스트림이란 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소(Sequence of elements)로 정의할 수 있다.

  • 연속된 요소: 컬렉션(Collection)과 마찬가지로 스트림(Streams)은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공한다. 컬렉션은 자료구조이므로 컬렉션에서는 시간과 공간의 복잡성과 관련된 요소 저장 및 접근 연산이 주를 이룬다. 반면 스트림은 filter, sorted, map처럼 표현 계산식이 주를 이룬다. 즉, 컬렉션의 주제는 데이터고, 스트림의 주제는 계산이다.

  • 소스: 스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비한다. 정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지된다. 즉, 리스트로 스트림을 만들면, 스트림의 요소는 리스트의 요소와 같은 순서를 유지한다.

  • 데이터 처리 연산: 스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원한다. 예를 들어 filter, map, reduce, find, match, sort 등으로 데이터를 조작할 수 있다. 스트림 연산은 순차적으로 또는 병렬로 실행할 수 있다.

B. 스트림 API의 특징

  • 저장소가 없음(No Storage): 스트림 자체는 데이터를 저장하지 않는다. 대신, 컬렉션, 배열, I/O 채널 등의 데이터 소스를 표현하는 데 사용된다.

  • 함수형(Functional in nature): 스트림 연산은 주로 람다 표현식을 사용하여 함수형 프로그래밍의 접근 방식을 취한다. 이로 인해 외부 반복(external iteration) 대신 내부 반복 (internal iteration)을 사용할 수 있다.

  • 지연성 추구(Laziness-seeking): 많은 스트림 연산들(filtering, mapping)은 지연된다는 특징을 갖고 있다. 즉, 실제로 필요할 때까지 계산을 수행하지 않는다.

  • 무한성(Possibly unbounded): 스트림은 한정된 데이터뿐만 아니라 무한한 데이터에 대해서도 작업을 수행할 수 있다. 예를 들어, 난수 스트림은 무한하다.

  • 소비 가능성(Consumable): 스트림의 요소들은 소비된다. 즉, 한 번 사용된 스트림은 재사용할 수 없다.

C. 스트림 API의 구성 요소

(1) 중간 연산

  • 중간 연산은 Java의 Stream API에서 스트림의 요소들을 처리하고, 변환하는 데 사용되는 연산이다. 이 연산들은 스트림을 다른 스트림으로 변환하며, 여러 중간 연산을 연결하여 복잡한 데이터 처리 파이프라인을 구축할 수 있다.

  • 중요한 특징은 이러한 연산들이 지연 평가되어, 실제로 최종 연산이 호출될 때까지 실행되지 않는다는 것이다. 이를 통해 효율적인 데이터 처리가 가능해지며, 필터링, 매핑, 정렬, 제한, 중복 제거 등 다양한 연산을 수행할 수 있다.

  • 각 중간 연산은 새로운 스트림을 반환하므로, 연속적으로 체이닝하여 사용할 수 있으며, 이러한 특성으로 인해 코드의 가독성과 유지보수성이 크게 향상된다.

(2) 최종 연산

  • 최종 연산은 Java의 Stream API에서 데이터 처리 파이프라인의 마지막 단계로, 스트림의 요소들에 대한 계산을 실행하고 결과를 도출하는 연산이다. 이 연산들은 스트림의 데이터 처리를 완료하며, 스트림을 소비한 후에는 더 이상 사용할 수 없다.

  • 최종 연산에는 요소들의 개수를 세는 count, 조건에 맞는 요소를 검사하는 anyMatch, allMatch, noneMatch, 요소들을 특정 방식으로 수집하는 collect, 요소들을 하나의 값으로 축소하는 reduce 등이 포함된다.

  • 최종 연산이 호출되면, 스트림의 모든 지연된 중간 연산이 실행되며, 최종적인 결과가 도출된다. 이는 스트림 API를 사용하여 복잡한 데이터 처리 로직을 선언적으로 표현할 수 있게 해주며, 더욱 간결하고 의도를 명확히 드러내는 코드를 작성할 수 있게 한다.

  • 최종 연산:

4. Reference

  • “모던 자바 인 액션” (저자: 라울-게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로프트)

태그:

카테고리:

업데이트:

댓글남기기