스트림 소개

  • 스트림을 이용하면 선언형으로 컬렉션 데이터 처리 가능
  • 병렬 처리 기능

예시

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

// 2. 정렬
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
    public int compare(Dish d1, Dish d2) {
        return Integer.compare(d1.getCalories(), d2.getCalories());
    }
});

//3. 요리 이름 추출
List<String> lowCaloricDisheName = new ArrayList<>();
for(Dish d: lowCaloricDishes) {
    lowCaloricDishesName.add(d.getName());
}
  • 구조를 파악하기 위해서 모든 for loop의 몸통을 살펴야함

자바 8은 세부구현을 라이브러리 내에서 모두 처리한다.

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

병럴 처리시, stream대신 parallelStream() 사용 - 7장

스트림 장점

  1. 선언형 (가독성)
  2. 조립 (유연성)
  3. 병렬화 (성능)
  4. 파이프 라인 형성
파이프라인

파이프라인을 쓰면, 컴퓨터 구조는 프로세서가 산술연산을 수행하는 동안에 다음번 명령어를 가져올 수 있으며,
그것을 다음 명령어 연산이 수행될 수 있을 때까지 프로세서 근처의 버퍼에 가져다놓는다.
명령어를 가져오는 단계는 끊임없이 계속된다. 그 결과, 주어진 시간동안에 수행될 수 있는 명령어의 수가 증가한다.

명령어1이 실행되는 동안 명령어2가 준비되고 실행되는 것

4.2 컬렉션 스트림

  • Collection에서는 stream을 반환하는 stream 메서드
  • 스트림이란?

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

  • 데이터 처리 연산
    • 람다
    • 디비와 비슷한 연산 지원 (filter, map, reduce, find, match, sort 등)으로 데이터 조작
    • 순차, 병렬 실행 가능
  • 소스
    • 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비
    • 리스트로 스트림을 만들면 순서 유지됨
  • 파이프라이닝
    • 스트림 반환
      • laziness, short-circuiting같은 최적화를 얻을 수 있다 (5장)
    • 연산 파이프라인은 데이터 소스에 적용하는 디비 질의와 비슷
  • 연속된 요소
    • 컬렉션 : 요소 저장, 접근연산
    • 스트림 : 요소 계산

내부 반복

  • 스트림은 내부 반복을 지원한다.

    스트림 연산

    • filter
      • 람다를 인수로 받아 스트림에서 특정 요소를 제외시킨다.
    • map
      • 람다를 이용해서 한 요소를 다른 요소로 변호나하거나, 정보 추출
    • limit
      • 스크림 크기 축소
    • collect
      • 스트림을 다른 형식으로 변환

4.3 스트림과 컬렉션

  • 둘다 연속된 요소 형식의 값을 저장
  • 컬렉션
    • 모든 값을 메모리에 저장
    • 컬렉션의 모든 요소는 추가되기 전에 계산되어야한다.
  • 스트림

    • 사용자가 요청한 값만 스트림에서 추출
    • 스트림에 요소를 추가, 제거 X
    • 게으르게 만들어지는 컬렉션과 같다.
      • 사용자가 데이터를 요청할 때만 값을 계산
  • 소수 예제

    • 컬렉션은 끝이 없는 모든 소수를 포함하려 할 것 이므로, 무한루프를 돌며 새로운 소수를 계산하고 추가하기 반복
      • 영원히 볼 수 없다
  • 브라우저 인터넷 검색 예제

    • 검색어를 입력하면 모든 검색 결과를 내려받는 것이아니라 10~20개의 결과 요소를 포함하는 스트림을 얻는다.

4.3.1 스트림은 한번만 탐색할 수 있다.

- 탐색된 요소는 소비
- 다시 탐색하려면 초기 데이터 소스에서 `새로운`스트림 생성해야함. 이때, 데이터솟는 반복을 사용할 수 있어야한다.
    - 데이터 소스가 I/O 채널이면 소스 반복X, 새루은 스트림 생성X
  • 내부 반복의 이점

    • 요소를 어떻게 반복시킬지는 신경쓰지 않고, 요소 처리 코드에만 집중 할 수 있다.

      • 외부 반복자는 요소를 가져오고, 처리하는 것까지 개발자가 작성, 그러나 스트림은 람다식으로 요소 처리 내용만 전달하고, 반복은 컬렉션 내부에서 일어난다.
    • 작업을 투명하게 병렬로 처리

    • 더 최적화된 다양한 순서로 처리
    • 데이터 표현과 하드웨어를 활용한 병렬성 구현을 자동으로 선택한다.
      • 외부 반복(for-each)은 병렬성 포기 or synchronized 사용
    • 요소의 반복 순서를 변경하거나, 멀티 코어 cpu를 최대한 활용하기 위해서 요소를 분배시켜 병렬 작업을 할 수 있도록 도와주기 때문에 순차적 외부 반복자보다 효율적으로 반복시킬 수 있다.
병렬 처리란 (parallel)?
한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리하는것

병렬 처리 스트림
런타임 시 하나의 작업을 서브 작업으로 자동으로 나누고, 서브 작업의 결과를 자동으로 결합해 최종 결과물 생성

스트림 연산

중간 연산

  • 중간 연산은 새로운 스트림을 반환
  • 단말 연산을 스트림 파이프라인에 실행하기 전까지 아무 연산도 수행하지 않는다.
    • 게으르다
    • 중간 연산을 합친 다음에 합쳐진 중간 연산을 최종 연산으로 한번에 처리
  • limit : 쇼트커트
  • filter + map : 루프 퓨전

최종 연산

  • 스트림 파이프라인에서 결과를 도출(스트림이 아닌)
    • 단말 연산을 스트림 파이프라인에 실행하기 전까지 아무 연산도 수행하지 않는다.
    • 중간 연산 정보를 합친다음 스트림으로 입력받아 한번에 처리한다.
  • 최종 연산을 실행하면 스트림 파이프라인은 소비되고, 재사용 할 수 없다. (다시 초기 데이터에서 새로운 스트림을 만들어야함)
  • forEach : void반환
  • 스트림을 닫을 필요는 없지만, IO채널의 경우에는 close해줘야한다.

Stream 성능


'JAVA > JAVA8' 카테고리의 다른 글

[JAVA8] 자바8 기초  (0) 2018.04.30

+ Recent posts