[JAVA8] Stream

Stream 소개

  • 데이터를 담고 있는 저장소(컬렉션)가 아니다.
  • Functional in nature , 스트림이 처리하는 데이터 소스를 변경하지 않는다.
  • 스트림으로 처리하는 데이터는 오직 한 번만 처리한다.
    • (실시간으로 들어오는 데이터)무제한일 수도 있다.
  • 중개 오퍼레이션은 근본적으로 lazy하다.
  • 손쉽게 병렬 처리할 수 있다.
병렬 처리를 쓴다고 꼭 빨라지는 것은 아니다.

 

스트림 파이프라인

  • 0 또는 다수의 중개 오퍼레이션 (intermediate operation)과 한 개의 종료 오퍼레이션 (terminal operation)으로 구성한다.
  • 스트림의 데이터 소스는 오직 종료 오퍼레이션을 실행할 때에만 처리한다.

 

중개 오퍼레이션

  • Stream을 리턴한다.
  • Stateless / Stateful 오퍼레이션으로 더 상세하게 구분할 수도 있다.
    • 대부분은 Stateless지만 distinct나 sorted처럼 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Stateful 오퍼레이션이다.
  • filter , map , limit , skip , sorted , ...

 

종료 오퍼레이션

  • Stream을 리턴하지 않는다.
  • collect , allMatch , count , forEach , min , max , ...

        List<String> names = new ArrayList<>();
        names.add("jeong");
        names.add("park");
        names.add("baek");
        names.add("kim");
        names.add("lee");

//        Stream<String> strStream = names.stream().map(s -> s.toUpperCase());
        Stream<String> strStream = names.stream().map(String::toUpperCase);
        // 대문자로 변경된 데이터는 strStream에 담겨 있다.
        strStream.forEach(System.out::println);

        System.out.println("=============================");
        // names 의 데이터가 변경 되지 않은걸 볼 수 있다.
        names.forEach(System.out::println);

//        출력
//        JEONG
//        PARK
//        BAEK
//        KIM
//        LEE
//        =============================
//        jeong
//        park
//        baek
//        kim
//        lee

        List<String> names = new ArrayList<>();
        names.add("jeong");
        names.add("park");
        names.add("baek");
        names.add("kim");
        names.add("lee");

        // 출력 되지 않는다.
        // 종료형 오퍼레이터가 실행 되기 전에는 중개형 오퍼레이터는 실행 하지 않는다. 
        // (단지 정의만 한것이다.)
        names.stream().map((s) ->{
            System.out.println("종료형 오퍼래이터가 붙지 않았을 때 : " + s);
            return s.toUpperCase();
        });

        // 종료형 오퍼레이터 (collect)가 붙었을 때 
        List<String> collect = names.stream().map((s) ->{
                                System.out.println("종료형 오퍼래이터가 붙었을 때 : " + s);
                                return s.toUpperCase();
                            }).collect(Collectors.toList());

//        출력
//        종료형 오퍼래이터가 붙었을 때 : jeong
//        종료형 오퍼래이터가 붙었을 때 : park
//        종료형 오퍼래이터가 붙었을 때 : baek
//        종료형 오퍼래이터가 붙었을 때 : kim
//        종료형 오퍼래이터가 붙었을 때 : lee

종료형 오퍼레이터는 없지만 인스턴스가 사용될 때

        List<String> names = new ArrayList<>();
        names.add("jeong");
        names.add("park");
        names.add("baek");
        names.add("kim");
        names.add("lee");


        Stream<Object> collect3 = names.stream().map((s) ->{
            System.out.println("종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용되지 않을 때: " + s);
            return s.toUpperCase();
        });

        Stream<Object> collect4 = names.stream().map((s) ->{
            System.out.println("종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용될 때 : " + s);
            return s.toUpperCase();
        });
        collect4.forEach(System.out::println);
        
//        출력
//        종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용될 때 : jeong
//        JEONG
//        종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용될 때 : park
//        PARK
//        종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용될 때 : baek
//        BAEK
//        종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용될 때 : kim
//        KIM
//        종료형 오퍼래이터가 붙지 않고 , 인스턴스가 사용될 때 : lee
//        LEE
  • 인스턴스가 사용되지 않는 collect3은 중개형 오퍼레이터가 실행되지 않는다.
  • 인스턴스가 사용되는 collect4는 중개형 오퍼레이터가 실행 되지만 출력 순서가 조금 이상하다.
    • collect4의 forEach안에서 중개형 오퍼레이터를 실행하여 sout을 찍고 , forEach의 sout을 찍는 것 같다.

        List<String> names = new ArrayList<>();
        names.add("jeong");
        names.add("park");
        names.add("baek");
        names.add("kim");
        names.add("lee");

        List<String> collectTest = names.stream().map((s) -> {
            System.out.println("stream 처리 : " + s + " " + Thread.currentThread().getName());
            return s.toUpperCase();
        }).collect(Collectors.toList());

        // 병렬 처리
        // (spliterator의 trySplit을 사용하여 쪼개서 처리한다.)
        List<String> collectTest2 = names.parallelStream().map((s) -> {
            System.out.println("parallelStream 처리 : " + s + " " + Thread.currentThread().getName());
            return s.toUpperCase();
        }).collect(Collectors.toList());

//        출력
//        stream 처리 : jeong main
//        stream 처리 : park main
//        stream 처리 : baek main
//        stream 처리 : kim main
//        stream 처리 : lee main
//        parallelStream 처리 : baek main
//        parallelStream 처리 : lee ForkJoinPool.commonPool-worker-3
//        parallelStream 처리 : park ForkJoinPool.commonPool-worker-5
//        parallelStream 처리 : kim ForkJoinPool.commonPool-worker-3
//        parallelStream 처리 : jeong ForkJoinPool.commonPool-worker-5

참고

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

 

더 자바, Java 8 - 인프런

자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이

www.inflearn.com