자바

자바 - 스트림

개발자 포비 2024. 12. 9. 21:58

Java Stream

개요

스트림(Stream)은 데이터를 처리하기 위한 연속적인 흐름을 추상화한 인터페이스입니다.

스트림의 동작 단계

  1. 생성(Creation)
  2. 가공(Intermediate Operations)
  3. 소비(Terminal Operations)

예시:

List<String> names = Arrays.asList("Kim", "Park", "Lee", "Choi");

names.stream()                           // 생성
    .filter(name -> name.length() > 3)   // 가공
    .forEach(System.out::println);       // 소비

스트림의 특징

지연 연산 (Lazy Evaluation)

Stream<String> stream = names.stream()
    .filter(name -> {
        System.out.println("필터링: " + name);
        return name.length() > 3;
    })
    .map(name -> {
        System.out.println("매핑: " + name);
        return name.toUpperCase();
    });
// 여기까지는 실제 연산이 수행되지 않음

stream.forEach(System.out::println);  // 이때 실제 연산 수행

일회성 사용

Stream<String> stream = names.stream();
stream.forEach(System.out::println);
// stream.count(); // IllegalStateException 발생

장점

1) 가독성 향상

  // 기존 방식
  List<Integer> evenNumbers = new ArrayList<>();
  for (int i = 0; i < numbers.size(); i++) {
      if (numbers.get(i) % 2 == 0) {
          evenNumbers.add(numbers.get(i));
      }
  }

  // 스트림 사용
  List<Integer> evenNumbers = numbers.stream()
      .filter(n -> n % 2 == 0)
      .collect(Collectors.toList());

2) 코드 유연성

  // 조건 추가가 용이
  numbers.stream()
      .filter(n -> n % 2 == 0)
      .filter(n -> n > 10)
      .filter(n -> n < 100)
      .collect(Collectors.toList());

3) 간편한 병렬 처리

  numbers.parallelStream()
      .filter(n -> n % 2 == 0)
      .collect(Collectors.toList());

단점

1) 성능 오버헤드: 간단한 연산의 경우 전통적인 for문이 더 빠를 수 있음
2) 디버깅 어려움: 스택 트레이스가 복잡해져 오류 추적이 어려울 수 있음

활용 예시

List<User> users = Arrays.asList(
    new User("Kim", 25),
    new User("Park", 30),
    new User("Lee", 35)
);

// 나이가 30 이상인 사용자의 이름을 대문자로 변환하여 정렬
List<String> result = users.stream()
    .filter(user -> user.getAge() >= 30)
    .map(User::getName)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());