더 자바, Java 8

Stream

Stream

Stream 소개

Stream

  • sequence of elements supporting sequential and parallel aggregate operations
    • 순차적 및 병렬 집계 작업을 지원하는 요소 시퀀스
  • 데이터를 담고 있는 저장소(컬렉션)이 아니다.
  • Funtional in nature, 스트림이 처리하는 데이터 소스를 변경하지 않는다.
    • Funtional in nature : Funtional 하다 , 소스를 변경하지 않는다 라는 의미
  • 스트림으로 처리하는 데이터는 오직 한번만 처리한다.
  • 무제한일 수도 있다.(Short Circuit 메소드를 사용해서 제한할 수 있다.)
    • Short Circuit : 단축시키는 , 제한할 수 있다.
  • 중개 오퍼레이션은 근본적으로 lazy 하다.
  • 손쉽게 병렬 처리할 수 있다.
    • parallelStream()
      • 무조건 빨라지는게 아니다.
      • 오히려 context switch 를 하기 때문에 속도가 느려질 수 있다.
      • 데이터가 방대하게 클 경우에 유용하다.

        List<String> test = new ArrayList<>();
        test.add("test1");
        test.add("test2");
        test.add("test3");
        test.add("test4");

        List<String> collect = test.parallelStream().map((s) -> {
            System.out.println(s + " " + Thread.currentThread().getName());
            return s.toUpperCase();
        }).collect(Collectors.toList()); 
        collect.forEach(System.out::println);

스트림 파이프라인

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

중개 오퍼레이션

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

종료 오퍼레이션

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

참고

Stream API

걸러내기

  • Filter(Predicate)
  • 예) 이름이 3글자 이상인 데이터만 새로운 스트림으로

        List<String> test = new ArrayList<>();
        test.add("가가가");
        test.add("나나나");
        test.add("다다다");
        test.add("마마마마");

        Stream<String> stringStream = test.stream().filter(s -> s.length() > 3);



        System.out.println("spring 으로 시작하는 수업");
        // TODO
        springClasses.stream().filter(sc -> sc.getTitle().startsWith("spring")).forEach(sc -> System.out.println(sc.getTitle()));

        System.out.println("close 되지 않은 수업");
        // TODO
        springClasses.stream().filter(Predicate.not(OnlineClass::isClosed)).forEach(sc -> System.out.println(sc.getTitle()));

변경하기

  • Map(Function) 또는 FlatMap(Function)
  • 예) 각각의 Post 인스턴스에서 String title 만 새로운 스트림으로
  • 예) List<Stream>을 String 의 스트림으로
        List<Post> posts = new ArrayList<>();
        posts.add(new Post("첫번째 게시글", "첫번째 게시글의 내용"));
        posts.add(new Post("두번째 게시글", "두번째 게시글의 내용"));
        posts.add(new Post("세번째 게시글", "세번째 게시글의 내용"));
        posts.add(new Post("네번째 게시글", "네번째 게시글의 내용"));
        posts.add(new Post("다섯번째 게시글", "다섯번째 게시글의 내용"));
        posts.add(new Post("여섯번째 게시글", "여섯번째 게시글의 내용"));
        posts.add(new Post("일곱번째 게시글", "일곱번째 게시글의 내용"));
        posts.add(new Post("여덟번째 게시글", "여덟번째 게시글의 내용"));
        posts.stream().map(Post::getTitle).forEach(System.out::println);
        System.out.println("-------------------------------");
        posts.stream().flatMap(post -> post.getTitle().stream()).forEach(System.out::println);

        System.out.println("수업 이름만 모아서 스트림 만들기");
        // TODO
        Stream<String> stringStream = springClasses.stream().map(OnlineClass::getTitle);


        System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");
        // TODO
        sumEvents.stream().flatMap(Collection::stream).forEach(oc -> System.out.println(oc.getId()));

생성하기

  • generate(Supplier) 또는 Interate(T seed, UnaryOperator)
  • 예) 10 부터 1 씩 증가하는 무제한 숫자 스트림
  • 예) 랜덤 int 무제한 스트림

        Stream.iterate(10, i -> i + 1).forEach(System.out::println);

제한하기

  • limit(long) 또는 skip(long)
  • 예) 최대 5개의 요소가 담긴 스트림을 리턴한다.
  • 예) 앞에서 3 개를 뺀 나머지 스트림을 리턴한다.

        Stream.iterate(10, i -> i + 1).limit(5).forEach(System.out::println);
        Stream.iterate(10, i -> i + 1).skip(3).forEach(System.out::println);
        
        Stream.iterate(10, i -> i + 1).skip(3).limit(5).forEach(System.out::println);
        

스트림에 있는 데이터가 특정 조건을 만족하는지 확인

  • allMatch(), allMatch(), nonMatch()
  • 예) k 로 시작하는 문자열이 있는지 확인한다. (true 또는 false 를 리턴한다.)
  • 예) 스트림에 있는 모든 값이 10 보다 작은지 확인한다.

        boolean allMatch = Stream.of("k", "j", "i", "h", "g").allMatch(s -> s.startsWith("k"));
        boolean anyMatch = Stream.of("k", "j", "i", "h", "g").anyMatch(s -> s.startsWith("k"));
        boolean noneMatch = Stream.of("k", "j", "i", "h", "g").noneMatch(s -> s.startsWith("k"));
        System.out.println(allMatch);
        System.out.println(anyMatch);
        System.out.println(noneMatch);
        
        boolean allLessThan10 = Stream.of(1, 2, 3, 4, 5).allMatch(i -> i < 10);
        boolean allLessThan10 = Stream.of(1, 2, 3, 4, 5).noneMatch(i -> i < 10);

개수 세기

  • count()
  • 예) 10 보다 큰 수의 개수를 센다.
          long count = Stream.of(1, 2, 3, 4, 5, 11, 12).filter(n -> n > 10).count();
          System.out.println(count);      
    

스트림을 데이터 하나로 뭉치기

  • reduce(indentity, BjFunction), collect(), sum(), max()
  • 예) 모든 숫자 합 구하기
  • 예) 모든 데이터를 하나의 List 또는 Set에 옭겨 담기

        int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (a, b) -> a + b);
        System.out.println(sum);
        
        List<Integer> list = Stream.of(1, 2, 3, 4, 5).collect(Collectors.toList());
        System.out.println(list);
        
        Set<Integer> set = Stream.of(1, 2, 3, 4, 5).collect(Collectors.toSet());
        System.out.println(set);
        
        Optional<Integer> max = Stream.of(1, 2, 3, 4, 5).max((a, b) -> a.compareTo(b));
        System.out.println(max.get());
        
        Optional<Integer> max = Stream.of(1, 2, 3, 4, 5).max(Integer::compareTo);
        System.out.println(max.get());
        

        int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (a, b) -> a + b);
        System.out.println(sum);                
        

        System.out.println("스프링 수업 중에 제목에 spring이 들어간 것만 모아서 List로 만들기");
        // TODO
        springClasses.stream().map(sc -> sc.getTitle()).filter(s -> s.contains("spring")).collect(Collectors.toList());


© 2020. All rights reserved.

SIKSIK