스트림 파이프라인


대량의 데이터를 가공해서 축소하는 것을 일반적으로 리덕션(Reduction)이라고 하는데, 데이터의 합계, 평균값, 카운팅, 최대값, 최소값 등이 대표적인 리덕션의 결과물이라고 볼 수 있다. 그러나 컬렉션의 요소를 리덕션의 결과물로 바로 집계할 수 없을 경우에는 집계하기 좋도록 필터링, 매핑, 정렬, 그룹핑 등의 중간 처리가 필요하다.



1. 중간 처리와 최종 처리


스트림은 데이터의 필터링, 매핑, 정렬, 그룹핑 등의 중간 처리와 합계, 평균, 카운팅, 최대값, 최소값 등의 최종 처리를 파이프라인(pipeline)으로 해결한다. 파이프라인은 여러 개의 스트림이 연결되어 있는 구조를 말한다. 파이프라인에서 최종 처리를 제외하고는 모두 중간 처리 스트림이다.



중간 스트림이 생성될 때 요소들이 바로 중간 처리(필터링, 매핑, 정렬) 되는 것이 아니라 최종 처리가 시작되기 전까지 중간 처리는 지연(lazy) 된다. 최종 처리가 시작되면 비로소 컬렉션의 요소가 하나씩 중간 스트림에서 처리되고 최종 처리까지 오게 된다.

Stream 인터페이스에는 필터링, 매핑, 정렬 등의 많은 중간 처리 메소드가 있는데, 이 메소드들은 중간 처리된 스트림을 리턴한다. 그리고 이 스트림에서 다시 중간 처리 메소드를 호출해서 파이프라인을 형성하게 된다. 예를 들어 회원 컬렉션에서 남자만 필터링하는 중간 스트림을 연결하고, 다시 남자의 나이로 매핑하는 스트림을 연결한 후, 최종 남자 평균 나이를 집계한다면 다음 그림처럼 파이프라인이 형성된다.



Stream<Member> maleFemaleStream = list.stream();
Stream<Member> maleStream = maleFemaleStream.filter(m -> m.getSex() == Member.MALE);
IntStream ageStream = maleStream.mapToInt(Member::getAge);
OptionalDouble optionalDouble = ageStream.average();
double ageAvg - optionalDouble.getAsDouble();
/////////////////////////////////////////////////////////////////////////////////
double ageAvg = list.stream()
                .filter(m -> m.getSex() == Member.MALE)
                .mapToInt(Member :: getAge)
                .average()
                .getAsDouble();


import java.util.Arrays;
import java.util.List;

public class StreamPipelineExample{
  public static void main(String[] args){
    List<Member> memberList = Arrays.asList(
      new Member("홍길동", Member.MALE, 30),
      new Member("고길동", Member.MALE, 40),
      new Member("정길동", Member.FEMALE, 20),
      new Member("오길동", Member.MALE, 29)
    );

    double ageAvg = memberList.stream()
                .filter(m -> m.getSex() == Member.MALE)
                .mapToInt(Member :: getAge)
                .average()
                .getAsDouble();

    System.out.println("남자 평균 나이 : " + ageAvg);
  }
}
public class Member {
  public static int MALE = 0;
  public static int FEMALE = 1;

  private String name;
  private int sex;
  private int age;

  public Member(String name, int sex, int age){
    this.name = name;
    this.sex = sex;
    this.age = age;
  }

  public String getName() {return name;}
  public int getSex() {return sex;}
  public int getAge() {return age;}
}



2. 중간 처리 메소드와 최종 처리 메소드


  • OptionalXXX : 최종 처리 메소드
  • 공통 : Stream, IntStream, LongStream, DoubleStream 에서 모두 제공
종류 리턴타입 메소드(매개 변수) 소속된 인터페이스
중간 처리 필터링 Stream
IntStream
LongStream
DoubleStream
distinct() 공통
filter(...) 공통
매핑 flatMap(...) 공통
flatMapToDouble(...) Stream
flatMapToInt(...) Stream
flatMapToLong(...) Stream
map(...) 공통
mapToDouble(...) Stream, IntStream, LongStream
mapToInt(...) Stream, LongStream, DoubleStream
mapToLong(...) Stream, IntStream, DoubleStream
mapToObj(...) IntStream, LongStream, DoubleStream
asDoubleStream() IntStream, LongStream
asLongStream() IntStream
boxed() IntStream, LongStream, DoubleStream
정렬 sorted(...) 공통
루핑 peek(...) 공통
최종 처리 매칭 boolean allMatch(...) 공통
boolean anyMatch(...) 공통
boolean noneMatch(...) 공통
집계 long count() 공통
OptionalXXX findFirst() 공통
OptionalXXX max(...) 공통
OptionalXXX min(...) 공통
OptionalDouble average() IntStream, LongStream, DoubleStream
OptionalXXX reduce(...) 공통
int, long, double sum() IntStream, LongStream, DoubleStream
루핑 void forEach(...) 공통
수집 R collect(...) 공통