chapter4 - 스트림

public interface Stream<T> extends BaseStream<T,Stream<T>>
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

//순차 처리의 stream
List<String> lowCaloricDishesName = menu.stream()
                                      .filter( d -> d.getCalories() < 400 ) //400칼로리 이하의 요리 선택
                                      .sorted(comparing(Dish::getCalories)) // 칼로리로 요리 정렬
                                      .map(Dish::getName) //요리명 추출
                                      .collect(toList()); //모든 요리명을 리스트에 저장
//병렬 처리 stream
List<String> lowCaloricDishesName = menu.parallelStream()
                                      .filter( d -> d.getCalories() < 400 ) //400칼로리 이하의 요리 선택
                                      .sorted(comparing(Dish::getCalories)) // 칼로리로 요리 정렬
                                      .map(Dish::getName) //요리명 추출
                                      .collect(toList()); //모든 요리명을 리스트에 저장

4.2 스트림 시작하기

  • 패키지 : java.util.stream.Stream
  • 스트림이란? : '데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소'로 정의할 수 있다.
  1. 연속된 요소 : 컬렉션과 마찬가지로 스트림은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공한다. 그러나 스트림은 컬렉션이 시간과 공간의 복잡성과 관련된 요소 저장 및 접근연산이 주를 이룬다면, 반면 스트림은 filter, sorted, map처럼 표현 계산식이 주를 이룬다.
  2. 소스 : 스트림은 컬렉션, 배열, I/O자원 등의 데이터 제공 소스로부터 데이터를 소비한다. 데이터 처리 연산 : 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원한다.( filter, map, reduce, find, match, sort )
  3. 파이프라이닝 : 대부분의 스트림 연산은 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있도록 스트림 자신을 반환한다.
  4. 내부 반복 : 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복을 지원한다.
  5. filter : 람다를 인수로 받아 스트림에서 특정 요소를 제외시킨다.
  6. map : 람다를 이용해서 한요소를 다른 요소로 변환하거나 정보를 추출한다.
  7. limit : 정해진 개수 이상의 요소가 스트림에 저장되지 못하게 스트림 크기를 축소 한다.
  8. collect : 스트림을 다른 형식으로 변환한다.

4.3 스트림과 컬렉션

  • 적극적 생성 : 모든 값을 계산할때 까지 기다린다. ( 컬렉션 )
  • 게으른 생성 : 필요할때만 값을 계산한다는 의미. ( 스트림 )

4.3.1 딱 한 번만 탐색할 수 있다!

  • forEach
    List<String> title = Arrays.asList("java8", "In", "Action");
    Stream<String> s = title.stream();
    s.forEach(System.out::println);//각 단어 출력 
    s.forEach(System.out::println);//java.lang.IllegalStateException: 스트림이 이미 소비되었거나 닫힘.
    

    4.3.2 외부 반복과 내부 반복

  • 외부 반복 : 일반적인 반복문을 사용한 반복.
  • 내부 반복 : 내부적으로 알아서 처리 되는 반복.
    List<String> names = menu.stream()
                           .map(Dish::getName)
                           .collect(toList());
    

4.4 스트림 연산

  1. 중간 연산 : 서로 연결되어 파이프라인을 형성한다.
    1. filter
    2. map
    3. limit
  2. 최종 연산
    1. collect : 파이프라인을 실행한 다음에 닫는다.

4.4.1 중간연산

List<Dish> menu = Dish.menu;
List<String> names = menu.stream()
                        .filter( d -> {
                                        System.out.println("filtering : " + d.getName());
                                        return d.getCalories() > 300;
                                      }
                                )
                        .map( d -> {
                                      System.out.println("mapping : " + d.getName());
                                      return d.getName();
                                   }
                             )
                        .limit(3)
                        .collect(toList());
System.out.println(names);
  • 결과
    filtering : pork
    mapping : pork
    filtering : beef
    mapping : beef
    filtering : chicken
    mapping : chicken
    [pork, beef, chicken]
    
  • 루프퓨전 : filter, map과 같이 서로 다른 연산이 한과정으로 병합 되는 기법.

4.4.2 최종연산

  • 최종 연산은 스트림 파이프라인에서 결과를 도출한다. 보통 스트림 이외의 결과가 반환된다.

4.4.3 스트림 이용하기

  • 스트림 이용과정
    • 질의를 수행할 ( 컬렉션 같은 ) 데이터 소스
    • 스트림 파이프라인을 구성할 중간 연산 연결
    • 스트림 파이프라인을 실행하고 결과를 만들 최종 연산
  • 많이 사용하는 중간 연산
연산 형식 반환 형식 연산의 인수 함수 디스크립터
filter 중간 연산 Stream<T> Predicate<T> boolean
map 중간 연산 Stream<T> Function<T,R> T -> R
limit 중간 연산 Stream<T>
sorted 중간 연산 Stream<T> Comparator<T> (T,T) -> int
distinct 중간 연산 Stream<T>  
  • 많이 사용하는 최종 연산
연산 형식 목적
forEach 최종연산 스트림의 각 요소를 소비하면서 람다를 적용한다. void를 반환한다.
count 최종연산 스트림의 요소 개수를 반환한다. long을 반환한다.
collect 최종연산 스트림을 리듀스해서 리스트,맵,정수 형식의 컬렉션을 만든다.