자바

[JAVA] Stream API

planting grass 2025. 3. 19. 18:02
728x90

Stream이란?

Stream(스트림)은 Java 8에서 도입된 연속적인 데이터 흐름을 처리하는 API다.
기존의 컬렉션을 반복문 없이 선언적(Declarative) 방식으로 변환, 필터링, 집계할 수 있도록 도와준다.

스트림은 데이터 구조가 아니기 때문에 컬렉션, 배열, 파일, IO 등에서 데이터를 받아 가공, 처리하는 역할을 한다.

즉, 스트림은 데이터 저장이 아닌 데이터 흐름을 처리하는 도구라고 생각하면 된다.

스트림은 함수형 프로그래밍 스타일을 지원하기 때문에, 람다식을 사용한다.

또한, 위에 말했듯이 컬렉션을 사용하기 때문에 컬렉션 프레임워크와도 관계되어 있다.

컬렉션 프레임워크는 블로그 내 정리해둔 글이 있으니 참고하면 된다.

https://lold2424.tistory.com/183

 

[JAVA] 컬렉션 프레임워크(Collection Framework)

컬렉션 프레임워크란?컬렉션 프레임워크(Collection Framework)는 Java에서 데이터 구조를 효율적으로 다룰 수 있도록 설계된 클래스와 인터페이스의 모음이다.이를 통해 다양한 자료구조를 쉽게 구현

lold2424.tistory.com

목적

  • 컬렉션을 더 효율적으로 처리하기 위해
  • 코드를 간결하게 작성하고 가독성을 높여줌
  • 병렬 처리를 지원하여 성능 향상 가능
  • 기존 반복문 기반 코드보다 최적화된 데이터 처리 가능

특징

  • 데이터 원본을 변경하지 않음
List<String> names = List.of("apple", "banana", "cherry");
names.stream()
     .map(String::toUpperCase)
     .forEach(System.out::println);
// 원본 리스트는 변경되지 않음
  • 내부 반복 사용
    • for문을 사용하는 외부 반복이 아닌, 내부 반복을 통해 자동으로 데이터를 처리한다.
    • 병렬 처리와 결합이 가능하다.
  • 중간 연산, 최종 연산
    • 중간 연산: 스트림을 변환 or 필터링하는 연산(여러 개 연결이 가능하다.)
      • map(), filter(), sorted(), distinct()
    • 최종 연산: 스트림을 소비하고 결과를 반환하는 연산(한 번만 사용이 가능하다.)
      • forEach(), collect(), reduce(), count(), min(), max()
  • 스트림은 1회 사용 후 재사용이 불가능하다.
  • 병렬 처리를 지원한다.
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.parallelStream()
                 .reduce(0, Integer::sum);

System.out.println(sum); // 55

중간 연산 사용 예시

메서드 설명
filter(Predicate<T>) 특정 조건을 만족하는 요소만 필터링
map(Function<T, R>) 요소를 변환 (타입 변경 가능)
flatMap(Function<T, Stream<R>>) 다중 컬렉션을 단일 스트림으로 평탄화
distinct() 중복 제거
sorted() 기본 정렬 (Comparable 기준)
sorted(Comparator<T>) 사용자 정의 정렬
peek(Consumer<T>) 요소를 중간에 확인 (디버깅용)
limit(long n) 최대 n개 요소만 가져옴
skip(long n) 처음 n개 요소를 건너뜀

1. filter(): 특정 조건의 요소만 필터링

List<Integer> numbers = List.of(10, 15, 20, 25, 30);

List<Integer> filtered = numbers.stream()
                                .filter(n -> n > 15)
                                .collect(Collectors.toList());

System.out.println(filtered); // [20, 25, 30]

2. map(): 각 요소를 변환

List<String> words = List.of("apple", "banana", "cherry");

List<String> upperCaseWords = words.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());

System.out.println(upperCaseWords); // [APPLE, BANANA, CHERRY]

3. flatMap(): 다중 리스트 평탄화

List<List<String>> nestedList = List.of(
    List.of("a", "b"),
    List.of("c", "d"),
    List.of("e", "f")
);

List<String> flatList = nestedList.stream()
                                  .flatMap(Collection::stream)
                                  .collect(Collectors.toList());

System.out.println(flatList); // [a, b, c, d, e, f]

4. distinct(): 중복 요소 제거

List<Integer> numbers = List.of(1, 2, 2, 3, 4, 4, 5);

List<Integer> uniqueNumbers = numbers.stream()
                                     .distinct()
                                     .collect(Collectors.toList());

System.out.println(uniqueNumbers); // [1, 2, 3, 4, 5]

5. sorted(): 요소 정렬

List<String> names = List.of("banana", "apple", "cherry");

List<String> sortedNames = names.stream()
                                .sorted()
                                .collect(Collectors.toList());

System.out.println(sortedNames); // [apple, banana, cherry]

최종 연산 사용 예시

메서드 설명
forEach(Consumer<T>) 각 요소를 반복 실행
toArray() 배열로 변환
collect(Collector<T, A, R>) 결과를 컬렉션으로 변환
count() 요소 개수 반환
anyMatch(Predicate<T>) 조건을 만족하는 요소가 하나라도 있는지 검사
allMatch(Predicate<T>) 모든 요소가 조건을 만족하는지 검사
noneMatch(Predicate<T>) 조건을 만족하는 요소가 없는지 검사
findFirst() 첫 번째 요소 반환 (Optional)
findAny() 아무 요소나 반환 (병렬 처리 시 유용)
min(Comparator<T>) 최소값 반환
max(Comparator<T>) 최대값 반환
reduce(BinaryOperator<T>) 모든 요소를 하나의 값으로 집계

1. forEach(): 요소 출력

List<String> names = List.of("apple", "banana", "cherry");

names.stream().forEach(System.out::println);

2. collect(): 리스트로 변환

List<String> names = List.of("apple", "banana", "cherry");

Set<String> nameSet = names.stream()
                           .collect(Collectors.toSet());

System.out.println(nameSet); // [apple, banana, cherry]

3. count(): 요소 개수 구하기

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

long count = numbers.stream().count();

System.out.println(count); // 5

4. anyMatch(), allMatch(), noneMatch(): 조건 검사

List<Integer> numbers = List.of(2, 4, 6, 8);

boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0); // 하나라도 짝수?
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0); // 모두 짝수?
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0); // 음수가 없는가?

System.out.println(anyEven);   // true
System.out.println(allEven);   // true
System.out.println(noneNegative); // true

5. reduce(): 요소 합산

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

int sum = numbers.stream()
                 .reduce(0, Integer::sum);

System.out.println(sum); // 15
728x90