Back_End/JAVA

자바(Java) 스트림(Stream) 이란?

10Biliion 2025. 2. 13. 08:41

 

 

1. Stream이란?

Java Stream은 컬렉션 데이터를 함수형 스타일로 처리할 수 있도록 도와주는 API이다. 기존의 반복문을 사용한 컬렉션 처리보다 간결하고 가독성이 높으며, 병렬 처리도 용이하다.

특징

  • 선언형 스타일: 코드가 간결하고 가독성이 좋다.
  • 중간 연산과 최종 연산: 연산을 구분하여 체이닝 가능
  • 내부 반복 처리: for 문 없이 요소를 처리
  • 병렬 처리 가능: parallelStream() 사용 시 병렬 실행 지원

2. Stream 생성 방법

Stream을 생성하는 방법은 여러 가지가 있으며, 주로 컬렉션이나 배열을 기반으로 생성한다.

2.1 컬렉션에서 생성

List<String> list = List.of("Apple", "Banana", "Cherry");
Stream<String> stream = list.stream();

2.2 배열에서 생성

String[] arr = {"A", "B", "C"};
Stream<String> stream = Arrays.stream(arr);

2.3 Stream 직접 생성

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

2.4 무한 스트림 (generate, iterate)

Stream<Double> randoms = Stream.generate(Math::random).limit(5);
Stream<Integer> numbers = Stream.iterate(1, n -> n + 2).limit(5);

3. Stream 연산 (중간 연산 & 최종 연산)

Stream은 **중간 연산(intermediate operation)**과 **최종 연산(terminal operation)**으로 구성된다.

3.1 중간 연산 (Intermediate Operations)

중간 연산은 스트림을 변환하는 연산이며, 최종 연산이 호출되기 전까지 실행되지 않는다 (Lazy Evaluation).

필터링 (filter)

List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.stream()
       .filter(n -> n % 2 == 0)
       .forEach(System.out::println); // 2, 4

매핑 (map)

List<String> words = List.of("java", "stream", "example");
words.stream()
     .map(String::toUpperCase)
     .forEach(System.out::println); // JAVA, STREAM, EXAMPLE

정렬 (sorted)

List<Integer> nums = List.of(5, 3, 8, 1);
nums.stream()
    .sorted()
    .forEach(System.out::println); // 1, 3, 5, 8

중복 제거 (distinct)

List<Integer> nums = List.of(1, 2, 2, 3, 4, 4, 5);
nums.stream()
    .distinct()
    .forEach(System.out::println); // 1, 2, 3, 4, 5

제한 (limit) 및 건너뛰기 (skip)

Stream<Integer> infiniteStream = Stream.iterate(1, n -> n + 1);
infiniteStream.limit(5).forEach(System.out::println); // 1, 2, 3, 4, 5

3.2 최종 연산 (Terminal Operations)

최종 연산은 스트림의 요소를 소비하고 결과를 반환하며, 한 번 실행되면 스트림은 종료된다.

요소 출력 (forEach)

List<String> items = List.of("A", "B", "C");
items.stream().forEach(System.out::println);

개수 반환 (count)

long count = Stream.of(1, 2, 3, 4, 5).count();
System.out.println(count); // 5

요소 찾기 (findFirst, findAny)

Optional<Integer> firstEven = Stream.of(3, 5, 8, 9)
                                   .filter(n -> n % 2 == 0)
                                   .findFirst();
System.out.println(firstEven.get()); // 8

조건 검사 (allMatch, anyMatch, noneMatch)

boolean allEven = Stream.of(2, 4, 6).allMatch(n -> n % 2 == 0);
boolean anyEven = Stream.of(1, 3, 5, 6).anyMatch(n -> n % 2 == 0);
System.out.println(allEven); // true
System.out.println(anyEven); // true

요소 수집 (collect)

List<String> list = Stream.of("a", "b", "c")
                         .collect(Collectors.toList());
System.out.println(list); // [a, b, c]

최소 / 최대 값 (min, max)

Optional<Integer> min = Stream.of(3, 1, 4, 1, 5).min(Integer::compareTo);
System.out.println(min.get()); // 1

합계 및 평균 (reduce)

int sum = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
System.out.println(sum); // 10

4. 병렬 스트림 (Parallel Stream)

Stream은 기본적으로 **순차 스트림(Sequential Stream)**으로 동작하지만, 병렬 스트림을 사용하면 멀티코어 프로세서를 활용하여 성능을 향상시킬 수 있다.

4.1 병렬 스트림 생성

List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.parallelStream()
       .forEach(System.out::println); // 병렬로 실행됨

4.2 주의할 점

  • 데이터 크기가 작다면 오히려 오버헤드로 인해 성능이 떨어질 수 있음
  • 공유된 가변 상태를 변경하는 경우 동기화 문제가 발생할 수 있음

5. 정리

연산 설명
filter 조건에 맞는 요소 필터링
map 요소를 변환
sorted 정렬 수행
distinct 중복 제거
limit / skip 요소 개수 제한 또는 건너뛰기
forEach 모든 요소 처리
collect 결과를 컬렉션으로 변환
reduce 요소를 하나의 값으로 축소

 

 

Java Stream을 활용하면 가독성과 유지보수성을 높이면서도 강력한 데이터 처리 기능을 구현할 수 있다.