본문 바로가기
Java Study

Stream

by xogns93 2024. 11. 5.

Stream은 Java 8에서 도입된 기능으로, 데이터 컬렉션을 처리하기 위한 새로운 방식을 제공합니다. Stream API를 사용하면 데이터를 선언적으로 처리할 수 있으며, 주로 컬렉션이나 배열의 요소를 반복적으로 처리하는 데 사용됩니다.

1. stream() 메서드란?

stream() 메서드는 컬렉션(Collection)이나 배열(Array) 같은 데이터 소스에서 Stream을 생성하는 메서드입니다. 이를 통해 데이터를 가공하거나 필터링, 매핑 등의 작업을 할 수 있는 스트림 파이프라인을 만들 수 있습니다.

예를 들어, List와 같은 컬렉션 객체에서 stream() 메서드를 호출하면 해당 컬렉션의 요소를 처리할 수 있는 Stream이 생성됩니다.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> nameStream = names.stream(); // 컬렉션에서 Stream 생성

2. Stream의 주요 특징

  • 선언적 스타일: forwhile과 같은 반복문을 사용하지 않고, map(), filter(), collect() 같은 메서드를 통해 무엇을 할지(what)에 집중한 선언적인 스타일로 코드 작성이 가능합니다.
  • 재사용 불가능: 스트림은 한 번 사용하면 다시 사용할 수 없습니다. 즉, 한 번의 파이프라인 작업을 수행하고 나면 소모되므로 새로운 스트림을 생성해야 합니다.
  • 지연 연산(Lazy Evaluation): 스트림의 일부 연산은 필요할 때까지 수행되지 않습니다. 예를 들어, 중간 연산은 결과를 즉시 반환하지 않고 최종 연산이 호출될 때 한꺼번에 처리됩니다.
  • 병렬 처리 지원: parallelStream()을 사용하면 멀티코어 CPU에서 스트림 요소를 병렬로 처리할 수 있어, 대량의 데이터에 대해 성능을 개선할 수 있습니다.

3. Stream API의 주요 메서드

Stream API에는 데이터를 가공하고 필터링하며 조합할 수 있는 다양한 메서드가 제공됩니다. 이들은 중간 연산최종 연산으로 구분됩니다.

중간 연산 (Intermediate Operation)

중간 연산은 다른 스트림을 반환하며, 지연 연산으로 동작합니다. 이러한 연산은 최종 연산이 실행될 때 실제로 수행됩니다.

  • filter(Predicate): 조건에 맞는 요소만을 필터링합니다.
  • map(Function): 각 요소를 다른 값으로 매핑합니다. 예를 들어, 각 요소를 제곱하거나 특정 객체의 필드를 추출할 때 사용합니다.
  • distinct(): 중복 요소를 제거합니다.
  • sorted(): 요소를 정렬합니다.

예시:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
       .filter(n -> n % 2 == 0) // 짝수만 필터링
       .map(n -> n * n)         // 제곱으로 매핑
       .forEach(System.out::println); // 최종 연산: 출력

최종 연산 (Terminal Operation)

최종 연산은 스트림을 처리하고 결과를 반환하거나, 스트림을 소비하는 연산입니다. 최종 연산이 실행될 때 중간 연산이 모두 수행됩니다.

  • forEach(Consumer): 각 요소에 대해 작업을 수행합니다.
  • collect(Collector): 스트림의 요소를 다른 형태로 변환(예: 리스트나 맵으로 변환)합니다.
  • reduce(BinaryOperator): 스트림의 요소를 하나의 값으로 누적합니다.
  • count(): 요소의 개수를 반환합니다.
  • findFirst(), findAny(): 첫 번째 요소 또는 임의의 요소를 반환합니다.

예시:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> filteredNames = names.stream()
                                  .filter(name -> name.startsWith("A")) // A로 시작하는 이름 필터링
                                  .collect(Collectors.toList());         // 리스트로 수집

System.out.println(filteredNames); // 출력 결과: [Alice]

4. stream()parallelStream()

  • stream(): 요소를 순차적으로 처리하는 스트림을 생성합니다.
  • parallelStream(): 요소를 병렬로 처리하는 스트림을 생성하여, 멀티코어 환경에서 성능을 향상시킬 수 있습니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream() // 병렬로 처리
                 .mapToInt(Integer::intValue)
                 .sum();
System.out.println("Sum: " + sum); // 출력 결과: Sum: 15

5. Stream 사용의 장점

  • 간결한 코드: 반복문 없이 함수형 스타일로 데이터를 처리할 수 있어 코드가 간결해집니다.
  • 효율적인 데이터 처리: 대량의 데이터 처리가 필요할 때 스트림을 사용하면 병렬 처리를 통해 성능을 최적화할 수 있습니다.
  • 코드 가독성: 선언적 스타일로 코드의 의도가 명확해지므로 가독성이 높아집니다.

요약

stream() 메서드는 컬렉션이나 배열에서 스트림을 생성하여 데이터를 효과적으로 처리할 수 있게 해줍니다. Stream API는 중간 연산과 최종 연산을 조합하여 데이터를 필터링, 매핑, 변환할 수 있으며, 병렬 처리를 통해 성능을 개선할 수 있습니다. 이를 통해 Java에서 더 간결하고 선언적인 데이터 처리 방식을 제공합니다.