[자바 인 액션] Ch 4

Ericamoyed·2021년 4월 26일
0

자바인액션

목록 보기
4/6

스트림

소개

  • 자바 8 API에 새로 추가된 기능, 선언형으로 컬렉션 데이터 처리 가능
  • 멀티스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리
  • .filter(), .sorted(), .map(), .collect(), .groupingBy(), .reduce(), .find(), .match(), .limit(), .distinct() 등이 있음
  • 데이터 처리 과정을 병렬화하면서도 스레드와 락을 걱정할 필요 없음
  • 구아바 라이브러리 (우리도 쓰길래 뭔지 좀 봤음)
    • 구글에서 만든 라이브러리로 멀티맵, 멀티셋 등 추가적인 컨테이너 클래스를 제공해주는 라이브러리
  • 선언형, 조립 가능, 병렬화 요 세가지로 스트림 장점 요약 가능

컬렉션 스트림

  • stream 메서드는 컬렉션에서 스트림을 반환.
  • 스트림은 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소로 정의할 수 있음_
  • 리스트로 스트림을 만들어도 스트림 요소는 리스트 요소와 같은 순서를 유지한다.
  • 스트림 특징
    • 파이프라이닝: 대부분의 스트림 연산은 스트림 자신을 반환
    • 내부 반복

스트림 vs. 컬렉션

  • 컬렉션
    • 현재 자료구조가 포함하는 모든 값을 메모리에 저장
    • 컬렉션의 모든 요소는 컬렉션에 추가하기 전에 계산되어야 함
  • 스트림
    • 요청할 때만 요소를 계산 (lazy)
    • 따라서 무제한의 소수를 포함하는 스트림도 간단히 생성 가능
    • producer/consumer 관계라고 볼 수 있겠다
    • 예를들어 검색 시에 모든 검색 결과를 내려받을 때까지 기다리지 않아도 가장 비슷한 10~20개의 결과 요소를 포함하는 스트림을 얻을 수 있음

스트림의 특징

  • 딱 한번만 탐색할 수 있다.
    • 탐색된 스트림의 요소는 소비되어, 한번 탐색한 요소를 재탐색 하려면 초기 데이터 소스에서 새로운 스트림을 다시 만들어 작업해야한다.
    • 따라서 스트림을 만들고, s.forEach()를 두번 사용하게 되면, 두번째 s.forEach()에서 스트림이 이미 소비되었거나 닫혔다는 exception을 던지게 된다.

내부반복 vs. 외부반복

  • 외부 반복
    • 컬렉션 인터페이스 사용 시 for-each등을 사용해서 직접 요소를 반복하는 것
  • 내부 반복
    • 스트림 라이브러리에서 반복을 알아서 처리하고 결과 스트림 값을 어딘가 저장해주는 방식
    • ex) map도 그렇고, filter도 그렇고, ...
  • 내부 반복이 좋은 여러가지 이유
    • 반복하면서 한 손에는 인형을, 다른 한 손에는 공을 동시에 들 수 있다. (동시 처리(병렬 처리) 가능)
    • 모든 장난감을 상자 가까이 이동 시킨 후 장난감을 상자에 넣을 수 있음. (효율성, 최적화)
    • 스트림의 내부반복은 데이터 표현과 하드웨어를 활용한 병렬성 구현을 자동으로 선택하지만, 외부 반복에서는 병렬성을 스스로 관리해야함

스트림 연산

  • 중간 연산
    • 다른 스트림을 반환, 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다. (lazy),
    • 중간 연산을 합친 다음의 결과물을 가지고 최종 연산까지 한번에 수행함
    • 여기서 아주 놀라운 예시 등장 (자바 인 액션 151p. 참고)
    • 여러 스트림 연산이 줄지어져 있을 때, filter 끝나고 map 끝나고 limit 끝나고 collect 하는 구조가 아니라, filter/map을 합쳐서 처리함으로써 limit 갯수 만큼만 수행하는 방식.
    • 이렇게 되면서, filter에서 실제로 전체 데이터 대상으로 필터를 수행했다면 천만개, 그리고 map에서 천만개 이렇게 메모리를 낭비하지 않아도 초기부터 3개만 세팅해서 가져올 수 있다는 magic
      • 전부 돌지 않고 세개만 가져온 것: limit 연산, 쇼트 서킷
      • filter/map이 다른 연산이지만 한 과정으로 병합되어 수행된 것, 루프 퓨젼
  • 최종 연산
    • .count(), .collect() 등..
    • 몇개 없고 이미 잘 사용 중이기에 pass

결론

  • 스트림 요소는 lazy하게 처리된다.
  • 중간 연산을 이용해 pipeline은 구성할 수 있지만, 중간 연산 만으로는 어떤 결과도 생성할 수 없다.

궁금한 점

  • stream의 forEach와, 컬렉션의 forEach 성능 비교가 궁금해졌음. 나중에 찾아보고 채우겠음
profile
꿈많은 개발자, 일상 기록을 곁들인

0개의 댓글