1. Stream API란?

java 8부터 추가된 api로, 배열이나 콜렉션에 저장된 데이터를 처리하기 위해서 도입되었다. 다양한 방식으로 저장된 데이터(배열, 콜렉션, 파일에 저장된 데이터)를 처리하기 위한 공통된 처리방법을 제공한다.

Stream API는 스트림의 생성, 스트림의 중간 연산(스트림의 변환), 스트림의 최종 연산(스트림의 사용) 순서로 사용된다. stream의 동작 흐름에서 스트림 중간 연산은 2개 이상의 연산을 포함할 수 있고, 생략하는 것도 가능하다.

    List<Job> jobs = jobService.getAllJobs();

    jobs.stream() // 스트림 생성
        .map(job -> job.getMinSalary()) // 중간연산
        .distinct() // 중간연산
        .sorted() // 중간연산
        .collect(Collectors.toList());  // 최종연산

위 코드에서

  1. jobs.stream()은 Job 객체를 순서대로 처리하는 Stream<Job> 객체를 반환한다.
  2. map(job -> job.getMinSalary())은 정수를 순서대로 처리하는 Stream<Integer> 객체를 반환한다.
  3. distinct()는 중복된 정수를 제거한 Stream<Integer> 객체를 반환한다.
  4. sorted()는 오름차순 정렬된 Stream<Integer> 객체를 반환한다.
  5. collect(Collectors.toList())List<Integer> 객체를 반환한다.
    즉, 스트림 객체가 반환되지 않는 것이 최종 연산이다.

2. Stream API의 특징

  • 내부반복을 통해서 작업을 수행한다.
    List<Integer> numbers = List.of(10, 20, 40, 20, 50);

    // 외부 반복
    for (int num : numbers) {
        System.out.println(num);
    }

    // 내부 반복
    numbers.stream().forEach(num -> System.out.println(num));
  • 스트림은 일회성이다.
  • 스트림은 원본데이터를 변경하지 않는다. (stream()을 사용하면 원본데이터의 복사본을 사용하는 것이다)
  • 스트림은 병렬처리를 쉽게 처리할 수 있다.
  • 스트림은 다양한 데이터소스로부터 stream<T>를 생성할 수 있다.
    • 데이터소스란? 콜렉션, 배열, 파일, 지정된 범위의 연속된 정수, 난수 등
  • 사용 예시
      Stream<T> stream = 콜렉션.stream();
      Stream<T> stream = Arrays.stream(배열);
      IntStream stream = IntStream.range(1, 10); // 1 ~ 9 
      IntStream stream = IntStream.rangeClosed(1, 10); // 1 ~ 10
    

3. stream의 중간 연산

다양한 데이터소스로부터 생성된 초기 스트림을 다른 스트림으로 변환하는 연산이다. 중간 연산은 스트림을 전달받아 스트림을 반환하기 때문에 연속적으로 사용할 수 있다.

  • 중간 연산의 종류
    • 필터링: filter(), distinct()
    • 변환: map(), flatMap()
    • 제한: limit(), skip()
    • 정렬: sorted()
    public static void main(String[] args) {
        List<Score> scores = List.of(new Score(100, 100, 100), 
            (new Score(100, 90, 80));

        // 1. 스트림 생성
        Stream<Score> scoreStream = scores.stream();

        // 2. 스트림 중간 연산
        Stream<Integer> totalStream = scoreStream.map(score -> score.kor + score.eng + score.math);

        // 3. 스트림 최종 연산
        totalStream.forEach(total -> System.out.println(total));
    }

    static class Score {
        int kor;
        int math;
        int eng;

        // constructor 생략
    }

4. stream의 최종 연산

  • 스트림 최종 연산을 통해서 스트림의 각 요소를 소모하거나 결과를 표시한다.
  • 최종 연산이 완료된 스트림은 더 사용할 수 없다.
  • 최종 연산의 종류
    • 요소의 출력 : forEach()
    • 요소의 소모 : reduce()
    • 요소의 검색 : findFirst(), findAny()
    • 요소의 검사 : anyMatch(), allMatch(), noneMatch()
    • 요소의 통계 : count(), min(), max()
    • 요소의 연산 : sum(), average()
    • 요소의 수집 : collect()