Stream.generate(), Stream.iterate() 등💡 스트림은 데이터를 직접 저장하지 않으며, 원본 데이터를 변경하지 않는다 (Immutable)
스트림은 연산을 연결해 하나의 파이프라인을 구성
| 구분 | 예시 | 역할 | 개수 | 실행 시점 |
|---|---|---|---|---|
| 중간 연산 | filter, map, sorted, distinct, peek 등 | 데이터 변환/필터링 | 여러 개 가능 | 지연 실행 (lazy) |
| 최종 연산 | collect, forEach, reduce, count, anyMatch 등 | 결과 생성 | 스트림당 1개 | 호출 시 즉시 실행 |
Stream<Integer> s = list.stream()
.filter(v -> v > 10)
.map(v -> v * 2); // 아직 아무 연산도 실행되지 않음
List<Integer> result = s.collect(Collectors.toList()); // 이 시점에 전체 파이프라인 실행
list.stream()
.filter(v -> v > 10)
.map(v -> v * 2)
.findFirst(); // 최종 연산
findFirst()는 처음 조건을 만족하는 원소 하나만 필요
| 항목 | JavaScript map, filter | Java Stream |
|---|---|---|
| 처리 방식 | 각 함수마다 개별 루프 수행 | 파이프라인 최적화 → 1개의 루프로 처리 |
| 성능 최적화 | 거의 없음 | 단일 루프 + 내부 반복 최적화 |
| 병렬 처리 | 직접 관리 필요 | parallelStream() 자동 처리 |
예시: filter → filter → map이 있다면?
따라서 Stream은 대규모 데이터 처리에서 더 효율적
스트림은 parallel() 또는 parallelStream() 을 사용해서 병렬처리를 할 수 있다.
List<Integer> result = list.parallelStream()
.filter(v -> v > 10)
.map(v -> v * 2)
.collect(Collectors.toList());
Java 병렬 스트림은 내부적으로 Fork/Join Framework를 사용
분할 정복(Divide & Conquer) 패턴을 일반화한 프레임워크
대표적으로 병합 정렬, 퀵 정렬 같은 알고리즘에 사용됨
병렬 스트림은 ForkJoinPool이라는 스레드 풀을 사용
Runtime.getRuntime().availableProcessors() → CPU 코어 수만큼 워커 스레드 생성parallelStream() 호출 시, 이 공용 풀 위에서 작업이 돌아감그래서 같은 JVM 안에서 병렬 스트림을 과도하게 쓰면
다른 병렬 작업들과 스레드 풀을 두고 경쟁할 수 있다.
병렬 스트림은 원본 데이터를 여러 청크 로 나눠서 처리
parallelStream() 호출List, Array 같은 건 분할하기 쉽고 병렬 처리 효율이 좋음LinkedList처럼 인덱스 랜덤 접근이 안 되는 구조는 비효율적일 수 있음각 Task는 filter, map, sorted 같은 중간 연산들을 처리
parallelStream() 호출filter, map 등)을 실행forEachOrdered, 정렬, 순번 보장 등)parallelStream() 안에서 외부 리스트에 add 하는 코드 등