스트림은 주의해서 사용하라

김종준·2023년 7월 20일
0

이펙티브자바

목록 보기
38/63

스트림은 주의해서 사용하라

스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻한다.

스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다.

스트림 파이프라인은 소스 스트림에서 시작해 종단 연산으로 끝나며, 그사이에 하나 이상의 중간 연산이 있을 수 있다.

각 중간 연산은 스트림을 어떠한 방식으로 변환한다.

스트림 파이프라인은 지연 평가된다.

평가는 종단 연산이 호출될 때 이뤄지며, 종단 연산에 쓰이지 않는 데이터 원소는 계산에 쓰이지 않느다.

이러한 지연 평가가 무한 스트림을 다룰 수 있게 해주는 열쇠다.

종단 연산이 없는 스트림 파이프라인은 아무 일도 하지 않는 명령인 no-op과 같으니, 종단 연산을 빼먹는 일은 절대 없어야 한다.

스트림 API는 메서드 연쇄를 지원하는 플루언트 API다.

즉, 파이프라인 하나를 구성하는 모든 호출을 연결하여 단 하나의 표현식으로 완성할 수 있다.

파이프라인 여러 개를 연결해 표현식 하나로 만들 수도 있다.

기본적으로 스트림 파이프라인은 순차적으로 수행된다.

파이프라인을 병렬로 실행하려면 파이프라인을 구성하는 스트림 중 하나에서 parallel 메서드를 호출해 주기만 하면 되나, 효과를 볼 수 있는 상황은 많지 않다.

그리고 기존 코드는 스트림을 사용하도록 리팩터링을 하되, 새 코드가 나아 보일 때만 이를 반영하자.

스트림 파이프라인은 되풀이되는 계산을 함수 객체로 표현한다.

반면 반복 코드에서는 코드 블록을 사용해서 표현한다.

그런데 함수 객체로는 할 수 없지만 코드 블록으로는 할 수 있는 일들이 있다.

  1. 코드 블록에서는 범위 안의 지역변수를 읽고 수정할 수 있다.
    하지만 람다에서는 final이거나 사실상 final인 변수만 읽을 수 있고, 지역변수를 수정하는 것은 불가능하다.
  2. 코드 블록에서는 return 문을 사용해 메서드를 빠져나가거나, break나 contiune 문으로 블록 바깥으로 반복문을 종료하거나 반복을 한 번 건너뛸 수 있다.
    또한 메서드 선언에 명시된 검사 예외를 던질 수 있다.
    하지만 람다는 이중 어떤 것도 할 수 없다.

계산 로직에서 이상의 일들을 수행해야 한다면 스트림과는 맞지 않는 것이다.

반대로 다음 일들에는 스트림이 아주 안성맞춤이다.

  1. 원소들의 시퀀스를 일관되게 변환한다.
  2. 원소들의 시퀀스를 필터링한다.
  3. 원소들의 시퀀스를 하나의 연산을 사용해 결합한다.
  4. 원소들의 시퀀스를 컬렉션에 모든다.
  5. 원소들의 시퀀스에서 특정 조건을 만족하는 원소를 찾는다.

한편, 스트림으로 처리하기 어려운 일도 있다.

한 데이터가 파이프라인의 여러 단계를 통과할 때 이 데이터의 각 단계에서 값들에. 동시에 접근하기는 어려운 경우다.

스트림 파이프라인은 일단 한 값을 다른 값에 매핑하고 나면 원래의 값을 잃는 구조이기 때문이다.

원래 값고 새로운 값의 쌍을 저장하는 객체를 사용해 매핑하는 우회 방법도 있지만, 그리 만족스러운 해법은 아닐 것이다.

매핑 객체가 필요한 단계가 여러 곳이라면 특히 더 그렇다.

이런 방식은 코드 양도 많고 지저분하여 스트림을 쓰는 주목적에서 완전히 벗어난다.

가능한 경우라면, 앞 단계의 값이 필요할 때 매핑을 거꾸로 수행하는 방법이 나을 것이다.

0개의 댓글