Skip to content

Latest commit

 

History

History
98 lines (82 loc) · 5.72 KB

File metadata and controls

98 lines (82 loc) · 5.72 KB

Chapter4 스트림 소개

이 장의 내용

  • 스트림이란 무엇인가?
  • 컬렉션과 스트림
  • 내부 반복과 외부 반복
  • 중간 연산과 최종 연산

4.1 스트림이란 무엇인가?

스트림은 자바 API에 새로 추가된 기능으로,

  1. 선언형(임시구현 대신 질의로 표현)으로 컬렉션 데이터를 처리할 수 있다.
  2. 멀티 스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리할 수 있다.
  • 기존 코드 : p128 참고
  • 최신 코드
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName =
                menu.stream()       //병렬 실행 시 .parallelStream()으로 교체
                    .filter(d -> d.getCalories() < 400)
                    .sorted(comparing(Dish::getcalories))
                    .map(Dish::getName)
                    .collet(toList());

: for 루프와 if 조건문으로 어떻게 동작을 구현할지 지정하는 대신 getCalories() 라는 동작의 수행을 지정한다.
: filter, sorted, map, collect와 같은 여러 빌딩 블록 연산을 연결해 복잡한 데이터 처리 파이프라인을 만들 수 있다.
= 가독성과 명확성이 유지된다.

  • 자바 8 스트림 API의 특징 요약
  1. 선언형: 간결하고 가독성이 좋아진다.
  2. 조립할 수 있음: 유연성이 좋아진다.
  3. 병렬화: 성능이 좋아진다.

4.2 스트림 시작하기

스트림이란?
= 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소

  1. 연속된 요소: 컬렉션과 마차가지로 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스 제공. 컬렉션에서의 시공간의 복잡성과 관련된 요소 저장 및 접근 연산과 달리 스트림은 filter, sorted, map 과 같은 표현 계산식이 주를 이룬다.

  2. 소스: 스트림은 컬렉션, 배열, I/O 자원 등의 소스로부터 데이터를 소비한다.(컬렉션이나 리스트로 스트림을 만들면 정렬도 유지))

  3. 데이터 처리 연산: 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 DB와 비슷한 연산을 지원한다.(filter, map, reduce 등)

  4. 파이프라이닝: 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있다. 게으름, 쇼트서킷 같은 최적화를 얻을 수 있다.

  5. 내부 반복: 데이터 소스에서 연속된 요소를 스트림에 제공하고 나면 이어지는 일련의 데이터 처리 연산을 적용한다. 모든 연산은 스트림을 반환하여 이어지다가 collect 연산에서만 결과를 반환한다.

  6. filter: 람다를 인수로 받아 스트림에서 특정 요소를 제외시킨다.

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

  8. limit: 정해진 개수 이상의 요소가 스트림에 저장되지 못하게 스트림 크기를 축소한다.

  9. collect: 스트림을 다른 형식으로 변환한다.

4.3 스트림과 컬렉션

자바는 컬렉션과 스트림 모두 연속된 요소 형식의 값을 저장하는 자료구조의 인터페이스를 제공한다.
연속된 요소 형식 : 순차적으로 값에 접근한다는 것

  1. 스트림과 컬렉션의 차이 = 데이터를 언제 계산하느냐!

    • 컬렉션
      : 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조.
        모든 요소는 추가하기 전에 계산된다.
        생성자 중심으로 적극적으로 생성된다.
    • 스트림
      : 이론적으로 요청할 때만 요소를 계산하는 고정된 자료구조.
        스트림에 요소를 추가하거나 제거할 수 없다.
        생산자와 소비자(Producer-Consumer) 관계를 형성한다. 게으르게 만들어지는 컬렉션과 같다.
  2. 스트림은 딱 한번만 탐색할 수 있다.

    • 탐색된 스트림의 요소는 소비된다. 다시 탐색하려면 다시 스트림을 만들어야 한다.
    • 컬렉션처럼 반복 사용 가능한 데이터 소스가 아니면(Ex. IO 채널) 소스를 반복 사용할 수 없어 새로운 스트림을 만들 수 없다.
  3. 외부반복과 내부반복

    • 외부반복(Collection): 사용자가 직접 요소를 반복한다. 사용자가 병렬성을 직접 관리한다.
    • 내부반복(Stream): 어떤 일을 수행할지만 지정하면 반복을 알아서 처리하고 결과를 어딘가에 처리해준다. 작업을 투명하게, 병렬로, 최적화된 순서로 처리한다.

4.4 스트림 연산

중간연산과최종연산

  1. 중간 연산: 다른 스트림을 반환한다. 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다.
  2. 최종 연산: 스트림 파이프라인에서 결과를 도출한다.
  3. 스트림 이용과정
  • 질의를 수행할 (컬렉션과 같은) 데이터 소스
  • 스트림 파이프라인을 구성할 중간 연산 연결
  • 스트림 파이프라인을 실행하고 결과를 만들 최종 연산

** 빌더패턴

** Quiz

Q. 문제점을 찾아라

public static void main(String[] args) {
  List<String> myLocalList = new ArrayList<>();
  IntStream.rangeClosed(1, 10000)
           .parallel
           .forEach(i -> { 
                myLocalList.add(String.valueOf(i)); 
            });
}

A. ArrayList의 경우 Thread-safe 하지 않으므로 병렬(parallel)로 실행하면 일정한 값이 도출되지 않는다.
    예제의 경우 myLocalList.size()는 10000일수도 있고 아닐 수도 있다.