프로그래밍/JAVA

자바8 인 액션 스터디 정리(4장 스트림 소개)

빨강토끼 2017. 10. 11. 09:06

chapter 4. Introducing streams

4.1 What are Streams

  • 마치 SQL 쿼리 같이 데이터를 처리
  • 많은 요소를 포함하는 커다란 컬렉션을 단순히 처리

Before(Java 7)

List<Dish> lowCaloricDishes = new ArrayList<>();  
for(Dish d: menu){  
  if(d.getCalories() < 400){
    lowCaloricDishes.add(d);
  }
}

Collections.sort(lowCaloricDishes, new Comparator<Dish>() {  
  @Override
  public int compare(Dish d1, Dish d2) {
    return Integer.compare(d1.getCalories(), d2.getCalories());
  }
});

List<String> lowCaloricDishesName = new ArrayList<>();  
for(Dish d: lowCaloricDishes){  
  lowCaloricDishesName.add(d.getName());
}

자바8 코드

List<String> lowCaloricDishesName =  
  menu.stream()
  //menu.parallelStream()
  .filter(d -> d.getCalories() < 400)
  .sorted(Comparator.comparing(Dish::getCalories))
  .map(Dish::getName)
  .collect(toList());

4.2 Getting started with streams

  • Sequence of elements

    • Collections are about data

    • streams are about computations

  • Source
    • 정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지된다. 즉 리스트로 스트림을 만들면 스크림의 요소는 리스트의 요소와 같은 순서를 유지한다.
  • Data processing operations
    • 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원
    • 예) filter, map, reduce, find, match, sort 등
    • 스트림연산은 순차적으로 또는 병렬로 실행될 수 있다.

In addition

  • Pipelining
    • 스트림 연산 끼리 연결하여 커다란 파이프라인을 구성 할 수 있음.
    • 게으름(laziness), 쇼트서킷(sort-cicuiting) 같은 최적화를 얻을 수 있음.
    • 데이터베이스의 SQL 쿼리 와 비슷
  • Internal iteration
    • 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복을 지원
List<String> threeHightCaloricDishNames =  
            menu.stream()
            .filter(d -> d.getCalories() > 300)
            .map(Dish::getName)
            .limit(3)
            .collect(toList());
  • filter

    • 람다를 인수로 받아 스트림에서 특정 요소를 제외

    filter(d -> d.getCalories() > 300)

  • map

    • 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출

    map(Dish::getName)

  • limit

    • 정해진 개수 이상을 추출 못하게 한계를 지정

    limit(3)

  • collect

    • 스트림을 다른 형식으로 변환

    collect(toList());

4.3 Streams vs. Collections

  • DVD vs. Internet stream
  • 모든 값을 메모리에 저장하여 계산 vs. 필요할때만 요소를 계산
  • 브라우저 인터넷 검색

  • Traversavle only once

  List<String> title = Arrays.asList("java8", "In", "Action");
      Stream<String> s = title.stream();
      s.forEach(System.out::println);
      s.forEach(System.out::println);

  output :
  java8
  Exception in thread "main" In
  Action
  java.lang.IllegalStateException: stream has already been operated upon or closed
      at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:274)
      at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
      at Solution.IllegalStateException(Solution.java:77)
      at Solution.main(Solution.java:102)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:497)
      at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
  • External vs. internal iteration

    for-each loop

  List<String> names = new ArrayList<>();
  for(Dish d: menu){
    names.add(d.getName());
  }

iterator

  List<String> names = new ArrayList<>();
  Iterator<String> iterator = menu.iterator();
  while(iterator.hasNext()){
    Dish d = iterator.next();
    names.add(d.getName());
  }

stream: internal iteration

  List<String> names = menu.stream()
                           .map(Dish::getName)
                           .collct(toList());

4.4 Stream operations

  • intermediate operations

  • terminal operations

  • Intermediate operations

    • 다른 intermediate operations 와의 상호연동을 통하여 pipe line 을 형성한다.
    • intermediate operations의 동작들이 하나의 terminal operations으로 모이기 때문에 terminal operations이 호출되어서 파이프라인이 시작하기전 까지의 흐름이 동작하지 않는다( lazy)
    • 파이프라인이 어떻게 동작되고 있는지 확인하기 위하여 각각의 람다함수에 print 문을 삽입한다.(이러한 코딩은 흔히 사용되지 않지만 배우는 과정이기 때문에 특별한 경우이다.)
  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]
  1. 칼로리가 300보다 큰 dish 중에 오직 3개만 추출하고 동작이 끝난다.(Short-circuitinge)
  2. filter 와 map 의 명령이 하나의 경로로 합쳐져서 동작한다.(loop fusion)

    • Terminal operations
  3. Stream pipe 로의 결과를 처리한다.

  4. List 나 Integer, 혹은 void 등의 스트림이 아닌 다양한 포맷으로 데이터를 변환하여 리턴한다.

    menu.stream().forEach(System.out::println);

    • Working with streams
  5. Builder pattern 과 비슷

  6. intermediate operations
    • filter
    • map
    • limit
    • sorted
    • distinct
  7. Terminal operations
    • forEach
    • count
    • collect

Summary

  • stream은 데이터처리동작을 지원하는 source에서 연속된 요소이다.
  • Stream은 내부반복을 사용한다.
  • intermediate 와 terminal operations 이 있다.
  • intermediate operations 은 filter와 map 등 stream을 반환하고 다른 stream와 연동된다. 파이프라인이 설정될뿐 어떤 result 동작을 하지 않는다.
  • forEach 와 같은 terminal operations 은 스트림이 아닌 결과를 리턴한다.
  • 스트림의 요소는 요청시에 계산된다.