2023년 4월 2일조회 5641분 읽기
카테고리: JAVAJAVA - Stream
스트림(Stream)은 병렬 또는 순차적으로 처리할 수 있는 요소 시퀀스입니다.
컬렉션, 배열, 파일, 난수 등 다양한 소스에서 생성할 수 있으며 map, reduce, filter, sort 등의 기능적 스타일 작업을 지원합니다. 스트림은 지연(Lazy)되어 필요할 때만 요소를 계산합니다.
⭐️ 스트림의 특징
- 데이터 저장 없이 처리: 원본 데이터를 변경하지 않습니다.
- 재사용 불가: 스트림 사용 후 가비지 컬렉션(GC) 대상이 됩니다.
- 단일 처리: 데이터를 한 번에 하나씩 처리합니다.
- 병렬 처리 지원: 여러 스레드를 활용한 병렬 처리가 가능합니다.
💡 스트림 vs 일반 메서드
스트림의 map과 컬렉션의 유사 메서드는 중요한 차이점이 있습니다.
- stream.map: 스트림의 중간 연산으로 스트림을 반환하며 지연되어 수행됩니다.
- 컬렉션의 메서드: 예를 들어
replaceAll은 컬렉션을 반환하고 즉시 수행됩니다.
java
List<String> list = Arrays.asList("a", "b", "c");
// 스트림의 map
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
// 컬렉션의 replaceAll
list.replaceAll(String::toUpperCase);
System.out.println(list);
1️⃣ 스트림 생성 방법
-
컬렉션에서 생성
javaList<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.stream(); stream.forEach(System.out::println); -
배열에서 생성
javaint[] arr = {1, 2, 3, 4, 5}; IntStream intStream = Arrays.stream(arr); intStream.forEach(System.out::println); -
빌더 사용
javaStream<String> builderStream = Stream.<String>builder() .add("Apple") .add("Banana") .add("Melon") .build(); builderStream.forEach(System.out::println); -
generate() 메소드
javaStream<String> generateStream = Stream.generate(() -> "Hello") .limit(5); generateStream.forEach(System.out::println); -
iterate() 메소드
javaStream<Integer> iterateStream = Stream.iterate(100, n -> n + 10) .limit(5); iterateStream.forEach(System.out::println); -
empty() 메소드
javaStream<String> emptyStream = Stream.empty(); System.out.println(emptyStream.count()); // 0 -
기본 타입 스트림
javaIntStream intStream = IntStream.range(1, 10); intStream.forEach(System.out::println);
2️⃣ 스트림 연산
-
filter
javaList<String> names = Arrays.asList("Jun", "James", "Chris", "Uni"); names.stream() .filter(name -> name.startsWith("J")) .forEach(System.out::println); // Jun, James -
map
javaList<String> fruits = Arrays.asList("apple", "banana", "melon", "grape"); fruits.stream() .map(String::toUpperCase) .forEach(System.out::println); // APPLE, BANANA, MELON, GRAPE -
sorted
javaList<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2); numbers.stream() .sorted() .forEach(System.out::println); // 1, 2, 3, 4, 5 -
집계 연산 (count, sum, average, min, max)
javaint[] scores = {80, 90, 100, 70, 85}; IntSummaryStatistics stats = Arrays.stream(scores).summaryStatistics(); System.out.println("개수: " + stats.getCount()); System.out.println("합계: " + stats.getSum()); System.out.println("평균: " + stats.getAverage()); System.out.println("최대: " + stats.getMax()); System.out.println("최소: " + stats.getMin()); -
매칭 연산 (anyMatch, allMatch, noneMatch)
javaList<String> colors = Arrays.asList("red", "blue", "green", "yellow"); boolean result1 = colors.stream().anyMatch(color -> color.equals("red")); // true boolean result2 = colors.stream().allMatch(color -> color.length() == 4); // false boolean result3 = colors.stream().noneMatch(color -> color.startsWith("p")); // true System.out.println(result1); // true System.out.println(result2); // false System.out.println(result3); // true -
reduce
javaList<String> animals = Arrays.asList("dog", "cat", "bird", "fish"); String result4 = animals.stream() .reduce("", (a, b) -> a + b); System.out.println(result4); // dogcatbirdfish -
collect
javaList<String> fruits = Arrays.asList("apple", "banana", "melon", "grape"); List<String> result5 = fruits.stream() .filter(fruit -> fruit.length() == 5) .collect(Collectors.toList()); result5.forEach(System.out::println); // apple, grape
⭐️ 병렬 처리 (Parallel Processing)
병렬 처리는 대용량 데이터 처리에 유용하며 스트림 API에서도 지원됩니다. parallel() 메소드를 호출하면 ForkJoin 프레임워크를 사용하여 여러 스레드가 작업을 수행합니다.
java
import java.util.Arrays;
public class ParallelStreamExample {
public static void main(String[] args) {
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
long start = System.currentTimeMillis();
long count = Arrays.stream(numbers)
.parallel()
.filter(n -> n % 2 == 0)
.count();
long end = System.currentTimeMillis();
System.out.println("짝수의 개수: " + count);
System.out.println("걸린 시간(ms): " + (end - start));
}
}
장점
- 성능 향상: 멀티 코어 CPU를 활용하여 처리 속도를 높입니다.
- 효율적 자원 사용: 여러 작업을 동시에 처리하여 자원 활용도를 높입니다.
단점
- 동기화 문제: 스레드 간 데이터 공유 시 동기화가 필요합니다.
- 오버헤드: 병렬 처리로 인한 스레드 관리 비용이 발생할 수 있습니다.
- 처리 순서: 순서가 중요한 작업에는 적합하지 않을 수 있습니다.
정리
스트림은 데이터 처리의 효율성과 가독성을 높입니다. 병렬 처리 시 동기화 문제와 오버헤드를 고려해야 합니다. 중간 연산과 최종 연산의 차이를 이해하고 상황에 맞게 스트림을 활용하는 것이 중요합니다.