슬라이싱
슬라이싱 메서드는 필요한 데이터만 가져오는 데 유용하다.
필요한 데이터만 가져오기 때문에 스트림을 끝까지 읽지 않고도 필요한 데이터를 처리할 수 있어 처리 시간을 단축하거나 메모리 사용량을 줄일 수 있다.
이때 원하는 데이터를 얻기 위해서는 슬라이싱을 적용할 데이터는 이미 정렬되어 있어야 한다.
takeWhile
- 프레디케이트가 처음 참이 되는 지점부터 이후의 요소를 반환한다.
dropWhile
- 프레디케이트가 처음 거짓이 되는 지점까지 발견된 요소를 버린다.
limit
- 주어진 값 이하의 크기를 갖는 새로운 스트림을 반환한다.
- 스트림이 정렬되어 있으면 최대 요소 n개를 반환할 수 있다.
skip
- 처음 n개 요소를 제외한 스트림을 반환한다.
- n개 이하의 요소를 포함하는 스트림에
skip
을 호출하면 빈 스트림이 반환된다.
병렬 스트림에서의 문제
순서 의존
병렬 스트림은 데이터를 여러 스레드로 나누어 처리하기 때문에 순서를 보장하기 위해서는 추가적인 작업이 필요하다.
동기화 문제
병렬로 나누어진 스레드 간에 조건이 발생한 지점을 공유하고 동기화하는 것은 병렬 처리의 효율성을 떨어뜨린다.
조건에 따른 데이터 무효화
각각의 스레드에서 데이터를 처리하는 동안 한 스레드에서 조건을 만족하는 요소를 발견하면 나머지 스레드가 처리 중인 작업이 의미 없어질 수 있다.
매핑
데이터 처리 과정에서 자주 수행되는 연산이다.
map
- 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑된다.
flatMap
- 스트림의 각 요소를 다른 스트림으로 변환한 뒤, 이를 하나의 평면화된 스트림으로 합치는 작업
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
- 스트림의 각 요소를 다른 스트림으로 변환한 뒤, 이를 하나의 평면화된 스트림으로 합치는 작업
// flatMap 예시
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b", "c"),
Arrays.asList("d", "e"),
Arrays.asList("f", "g", "h")
);
List<String> flatList = nestedList.stream() // List<List> -> Stream<List>
.flatMap(List::stream)
// 스트림 요소 처리: List -> Stream
// 처리한 요소 Stream을 하나의 Stream으로 평면화
.toList();
리듀싱
모든 스트림 요소를 처리해서 값으로 도출하는 메서드.
두 값을 하나로 도출하는 불변형 연산이다.
공통 리듀스 패턴은 내장 메서드로 제공된다. ex) sum
, max
T reduce(T identity, BinaryOperator<T> accumulator);
- 처리 후 값은 이전 값이 된다.
- 초기 값을 설정하지 않는다면 반환값이 Optional이 된다.
컬렉터
도출하려는 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드다.
컬렉터 메서드 (Collectors)
요약
스트림에 있는 객체의 숫자 필드의 합계나 평균 등을 반환하는 연산
- 내장 메서드
maxBy
,minBy
counting
averagingInt
summarizingInt
- 두 개 이상의 연산을 한 번에 수행해야 하는 경우
- 한번 사용한 스트림은 재사용할 수 없다.
- 두 개 이상의 연산을 한 번에 수행해야 하는 경우
- 범용 리듀싱
reducing
reducing(T identity, BinaryOperator<T> op)
reduce
와 동일한 시그니처를 가지고 있다.
그룹화
버킷 개념(물건을 담을 수 있는 양동이)으로 생각하면 쉽다.
groupingBy
- 키를 분류할 수 있는
classifier
를 파라미터로 받는다. 추가적인 작업을 위해downstream
도 파라미터로 받을 수 있다. groupingBy(Function<? super T, ? extends K> classifier)
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
에서 고려사항downstream
- 일반적으로 분류 함수(
filtering
)와, 컬렉터(toList
) 를 인수로 받는다. - 매핑함수(
mapping
,flatMap
) 를 통해서 요소를 변환할 수도 있다. - 두 인수를 받는
groupingBy
를 이용해 다수준으로 그룹화를 할 수 있다.
- 일반적으로 분류 함수(
- 키를 분류할 수 있는
분할
프레디케이트를 분류 함수로 사용하는 특수한 그룹화 기능이다.
키의 형식은 Boolean이다. 그룹화와 유사하다.
컬렉터 인터페이스
public interface Collector<T, A, R>
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
- 타입
T
: 수집될 스트림 항목의 제네릭 형식A
: 누적자, 즉 수집 과정에서 중간 결과를 누적하는 객체의 형식R
: 수집 연산 결과 객체의 형식
- 메서드
supplier
: 수집 과정에서 빈 누적자 인스턴스를 만드는 파라미터가 없는 함수accumulator
: 리듀싱 연산을 수행하는 함수를 반환, 누적자(n-1까지 수집한 상태)와 n번째 요소를 함수에 적용한다. 함수의 반환값은 void, 즉 요소를 탐색하면서 적용하는 함수에 의해 누적자 내부상태가 바뀌므로 누적자가 어떤 값일지 단정할 수 없다.finisher
: 스트림 탐색을 끝내고 누적자 객체를 최종 결과로 변환하면서 누적 과정을 끝낼 때 호출할 함수를 반환한다.combiner
: 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이 결과를 어떻게 처리할지 정의한다.characteristics
: 스트림을 병렬로 리듀스 할 것인지 그리고 병렬로 리듀스 한다면 어떤 최적화를 선택해야 할지 힌트를 제공한다.
'자바' 카테고리의 다른 글
세마포어 (0) | 2025.03.06 |
---|---|
Kotlin Data Classes (0) | 2025.03.04 |
Thread Pool 정리 (1) | 2025.01.01 |
모던 자바 인 액션을 다시 읽으며 든 스트림에 대한 생각 정리 (0) | 2024.12.18 |
Thread와 Lock 관련된 개념 정리 (1) | 2024.09.28 |
슬라이싱
슬라이싱 메서드는 필요한 데이터만 가져오는 데 유용하다.
필요한 데이터만 가져오기 때문에 스트림을 끝까지 읽지 않고도 필요한 데이터를 처리할 수 있어 처리 시간을 단축하거나 메모리 사용량을 줄일 수 있다.
이때 원하는 데이터를 얻기 위해서는 슬라이싱을 적용할 데이터는 이미 정렬되어 있어야 한다.
takeWhile
- 프레디케이트가 처음 참이 되는 지점부터 이후의 요소를 반환한다.
dropWhile
- 프레디케이트가 처음 거짓이 되는 지점까지 발견된 요소를 버린다.
limit
- 주어진 값 이하의 크기를 갖는 새로운 스트림을 반환한다.
- 스트림이 정렬되어 있으면 최대 요소 n개를 반환할 수 있다.
skip
- 처음 n개 요소를 제외한 스트림을 반환한다.
- n개 이하의 요소를 포함하는 스트림에
skip
을 호출하면 빈 스트림이 반환된다.
병렬 스트림에서의 문제
순서 의존
병렬 스트림은 데이터를 여러 스레드로 나누어 처리하기 때문에 순서를 보장하기 위해서는 추가적인 작업이 필요하다.
동기화 문제
병렬로 나누어진 스레드 간에 조건이 발생한 지점을 공유하고 동기화하는 것은 병렬 처리의 효율성을 떨어뜨린다.
조건에 따른 데이터 무효화
각각의 스레드에서 데이터를 처리하는 동안 한 스레드에서 조건을 만족하는 요소를 발견하면 나머지 스레드가 처리 중인 작업이 의미 없어질 수 있다.
매핑
데이터 처리 과정에서 자주 수행되는 연산이다.
map
- 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑된다.
flatMap
- 스트림의 각 요소를 다른 스트림으로 변환한 뒤, 이를 하나의 평면화된 스트림으로 합치는 작업
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
- 스트림의 각 요소를 다른 스트림으로 변환한 뒤, 이를 하나의 평면화된 스트림으로 합치는 작업
// flatMap 예시
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b", "c"),
Arrays.asList("d", "e"),
Arrays.asList("f", "g", "h")
);
List<String> flatList = nestedList.stream() // List<List> -> Stream<List>
.flatMap(List::stream)
// 스트림 요소 처리: List -> Stream
// 처리한 요소 Stream을 하나의 Stream으로 평면화
.toList();
리듀싱
모든 스트림 요소를 처리해서 값으로 도출하는 메서드.
두 값을 하나로 도출하는 불변형 연산이다.
공통 리듀스 패턴은 내장 메서드로 제공된다. ex) sum
, max
T reduce(T identity, BinaryOperator<T> accumulator);
- 처리 후 값은 이전 값이 된다.
- 초기 값을 설정하지 않는다면 반환값이 Optional이 된다.
컬렉터
도출하려는 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드다.
컬렉터 메서드 (Collectors)
요약
스트림에 있는 객체의 숫자 필드의 합계나 평균 등을 반환하는 연산
- 내장 메서드
maxBy
,minBy
counting
averagingInt
summarizingInt
- 두 개 이상의 연산을 한 번에 수행해야 하는 경우
- 한번 사용한 스트림은 재사용할 수 없다.
- 두 개 이상의 연산을 한 번에 수행해야 하는 경우
- 범용 리듀싱
reducing
reducing(T identity, BinaryOperator<T> op)
reduce
와 동일한 시그니처를 가지고 있다.
그룹화
버킷 개념(물건을 담을 수 있는 양동이)으로 생각하면 쉽다.
groupingBy
- 키를 분류할 수 있는
classifier
를 파라미터로 받는다. 추가적인 작업을 위해downstream
도 파라미터로 받을 수 있다. groupingBy(Function<? super T, ? extends K> classifier)
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
에서 고려사항downstream
- 일반적으로 분류 함수(
filtering
)와, 컬렉터(toList
) 를 인수로 받는다. - 매핑함수(
mapping
,flatMap
) 를 통해서 요소를 변환할 수도 있다. - 두 인수를 받는
groupingBy
를 이용해 다수준으로 그룹화를 할 수 있다.
- 일반적으로 분류 함수(
- 키를 분류할 수 있는
분할
프레디케이트를 분류 함수로 사용하는 특수한 그룹화 기능이다.
키의 형식은 Boolean이다. 그룹화와 유사하다.
컬렉터 인터페이스
public interface Collector<T, A, R>
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
- 타입
T
: 수집될 스트림 항목의 제네릭 형식A
: 누적자, 즉 수집 과정에서 중간 결과를 누적하는 객체의 형식R
: 수집 연산 결과 객체의 형식
- 메서드
supplier
: 수집 과정에서 빈 누적자 인스턴스를 만드는 파라미터가 없는 함수accumulator
: 리듀싱 연산을 수행하는 함수를 반환, 누적자(n-1까지 수집한 상태)와 n번째 요소를 함수에 적용한다. 함수의 반환값은 void, 즉 요소를 탐색하면서 적용하는 함수에 의해 누적자 내부상태가 바뀌므로 누적자가 어떤 값일지 단정할 수 없다.finisher
: 스트림 탐색을 끝내고 누적자 객체를 최종 결과로 변환하면서 누적 과정을 끝낼 때 호출할 함수를 반환한다.combiner
: 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이 결과를 어떻게 처리할지 정의한다.characteristics
: 스트림을 병렬로 리듀스 할 것인지 그리고 병렬로 리듀스 한다면 어떤 최적화를 선택해야 할지 힌트를 제공한다.
'자바' 카테고리의 다른 글
세마포어 (0) | 2025.03.06 |
---|---|
Kotlin Data Classes (0) | 2025.03.04 |
Thread Pool 정리 (1) | 2025.01.01 |
모던 자바 인 액션을 다시 읽으며 든 스트림에 대한 생각 정리 (0) | 2024.12.18 |
Thread와 Lock 관련된 개념 정리 (1) | 2024.09.28 |