Stream API 란?
java 8에 도입된 기능으로, Collection(예: list, set, map 등)을 처리하는데 사용된다.
데이터의 연속적인 흐름으로, 람다 표현식과 함께 사용하여 매우 간결하고 효율적으로 데이터를 처리할 수 있다.
주요 특징은 다음과 같다.
- 선언적
스트림 API는 데이터를 어떻게 처리할지에 대한 '방법'보다는 '무엇을 할 것인지'에 초점을 맞춘다. - 함수형 프로그래밍
람다 표현식을 사용하여 더 간결하고 읽기 쉬운 코드를 작성할 수 있다. - 지연 연산
스트림의 중간 연산은 지연(lazy) 연산으로, 최종 연산이 호출될 때까지 실제로 수행되지 않는다.
최종연산이 호출될 때, 스트림 파이프라인 전체가 실행되며 데이터 처리가 수행된다. - 병렬 처리
스트림은 쉽게 병렬 처리를 할 수 있는 메서드를 제공한다.
ㄴparallelStream()을 사용하여 병렬 스트림을 생성할 수 있다. - 내부 반복
코드의 일관성과 관결성을 유지할 수 있다.
내부 반복은 최적화가 라이브러리 수준에서 이루어지며, 대규모 데이터셋이나 병렬 처리에 대해 더 나은 성능을 제공할 수 있다.
언제 Stream API를 쓰는가?
- 대량의 데이터에 대해 반복적인 작업을 수행할 때.
- 데이터 변환 및 필터링이 필요할 때.
- 데이터 집계나 통계가 필요할 때.
- 데이터의 특정 조건을 검사할 때.
전통적인 loop문(for, while) VS Stream API
비교 항목 | Stream API | for 문 |
가독성 | 선언적 스타일로 코드가 간결하고 명확함 | 익숙하고 직관적이나, 복잡한 로직에서는 코드가 장황해질 수 있음 |
함수형 프로그래밍 | 함수형 프로그래밍 스타일을 지원, 람다식과 메서드 참조 사용 가능 | 함수형 프로그래밍 스타일 지원 부족 |
병렬 처리 | 병렬 처리 쉽게 구현 (parallelStream() 사용) | 병렬 처리 구현이 복잡함 |
지연 연산 | 지연 연산으로 최적화 가능 | 즉시 연산 |
디버깅 용이성 | 디버깅이 상대적으로 어려울 수 있음 | 각 단계에서 변수 추적이 쉬움 |
상태 유지 | 상태 유지가 어렵고 비효율적일 수 있음 | 반복문 내부에서 상태 유지 및 수정이 용이함 |
제어 구조 | 제한적 (forEach 내부에서 break, continue 사용 불가) | 다양한 제어 구조 (break, continue, return 등) 사용 가능 |
- 스트림 API는 가독성, 병렬 처리, 함수형 프로그래밍 스타일, 지연 연산 등의 이점으로 인해 데이터 처리 작업에 매우 유용하다.
- 특히 복잡한 데이터 변환 및 필터링 작업에 적합하다
- for 문은 단순한 반복 작업, 상태 유지가 필요한 작업, 디버깅이 중요한 경우, 다양한 제어 구조를 사용해야 하는 경우에 더 적합하다.
Stream API 기본 사용
Stream 생성
예시)
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class CollectionStreamExample {
public static void main(String[] args) {
// Collection에서 스트림 생성
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
// Array에서 stream 생성
String[] array = {"apple", "banana", "cherry"};
Stream<String> stream1 = Arrays.stream(array); // Arrays.stream() 사용
stream1.forEach(System.out::println);
Stream<String> stream2 = Stream.of(array); // Stream.of() 사용
stream2.forEach(System.out::println);
// Range에서 stream 생성
IntStream rangeStream = IntStream.range(1, 10);
rangeStream.forEach(System.out::println);
}
}
Collection에서는 '컬렉션변수.stream()' 을 통해 스트림 생성이 가능하다.
Array에서는 'Arrays.stream(배열)' 또는 'Stream.of(배열)' 을 통해 생성 가능하다.
중간 연산
중간 연산은 스트림을 변환하거나 필터링하는 것이며, 또 다른 스트림을 반환한다.
중간 연산은 지연(lazy) 연산으로 최종 연산이 호출되기 전까지 실제로 수행되지 않는다.
다음은 자주 사용되는 중간 연산들이다.
filter()
조건에 맞는 요소만을 포함하는 새로운 스트림을 반환한다.
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
map()
각 요소를 주어진 함수에 따라 변환하여 새로운 스트림 반환한다.
List<String> upperCaseWords = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
distinct()
중복된 요소를 제거한 새로운 스트림을 반환한다.
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
sorted()
스트림의 요소를 정렬하여 새로운 스트림을 반환한다.
comparator를 사용하여 커스텀 정렬도 가능하다
List<String> sortedWords = words.stream()
.sorted()
.collect(Collectors.toList());
// 커스텀 정렬
List<String> sortedWordsByLength = words.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
limit()
스트림의 요소를 지정한 개수만큼만 포함하는 새로운 스트림을 반환한다.
List<Integer> limitedNumbers = numbers.stream()
.limit(5)
.collect(Collectors.toList());
skip()
처음 n개의 요소를 건너뛴 스트림을 반환한다.
List<Integer> skippedNumbers = numbers.stream()
.skip(3)
.collect(Collectors.toList());
최종 연산
스트림 요소를 소모하여 결과를 생성하는 연산을 말한다.
최종 연산이 호출된 후 스트림 요소들이 처리되고, 스트림은 더 이상 사용할 수 없다.
아래는 자주 사용되는 최종 연산들이다.
collect()
스트림 요소를 수집하여 컬렉션을 다른 형태로 변경한다.
List<String> collectedList = words.stream()
.collect(Collectors.toList());
forEach()
스트림 각 요소에 대해 주어직 작업을 수행한다. 결과를 반환하지는 않는다.
words.stream()
.forEach(System.out::println);
reduce()
스트림 요소들을 결합하여 하나의 값으로 만든다.
int sum = numbers.stream()
.reduce(0, Integer::sum);
min(), max()
스트림의 최소값 또는 최대값을 반환한다. 요소가 없으면 빈 Optional을 반환한다.
Optional<Integer> minNumber = numbers.stream()
.min(Integer::compareTo);
Optional<Integer> maxNumber = numbers.stream()
.max(Integer::compareTo);
'Language > Java' 카테고리의 다른 글
Promotion(자동형변환)과 Casting(명시적형변환), Upcasting vs Downcasting (0) | 2024.12.05 |
---|---|
String Interning (0) | 2024.12.04 |
String vs String Builder vs String Buffer (0) | 2024.12.03 |
Java는 Call by Reference가 존재하지 않는다. (0) | 2024.12.01 |
JVM(Java Virtual Machine)과 Java 컴파일 과정 (0) | 2024.11.29 |