[Java] 스트림(Stream)이란? 개념과 주요 메서드, 직렬/병렬 스트림
by 우와한개발자
[ 스트림 Stream ]
1. 스트림 Stream 이란?
- 스트림(Stream)은 요소들의 연속적인 집합을 나타냄.
- 자바의 스트림(Stream) API는 컬렉션과 배열과 같은 데이터를 처리하는 데 사용
- 주로 데이터 변환, 필터링, 정렬, 수집(가공한 데이터), 통계 등에 사용
- Java 8 부터 적용
- 성능이 좋기 때문에 반복문보다는 stream 사용 권장
2. 스트림 Stream 의 특징
- 선언적 프로그래밍
- 지연 평가(Lazy Evaluation) : 필요할 때만 연산 수행. 즉, 최종연산이 호출될 때까지 중간연산 수행하지 않아 불필요한 계산 줄이고 성능 향상
- 함수형 스타일 : 함수형 인터페이스 사용해 람다 표현식과 메서드 참조를 통해 가독성 향상
- 재사용 안됨 : 다시 사용하려면 새로운 스트림 생성 해야함.
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
// stream.forEach(System.out::println); // Error
3. 스트림 Stream 연산
1) 스트림 생성
| 생성 방법 | 코드 예시 | 설명 |
| 컬렉션 기반 | list.stream() | List, Set 등 컬렉션에서 스트림 생성 |
| 배열 기반 | Arrays.stream(arr) | 배열을 스트림으로 변환 (int[], String[] 등) |
| Builder 사용 | Stream<String>builder() .add("a") .add("b") .build() |
점진적으로 값 추가 후 스트림 생성 |
| Stream.of() | Stream.of(1, 2, 3) | 직접 요소를 지정하여 생성 |
| 병렬 스트림 | list.parallelStream() | 병렬 처리용 스트림 생성 |
| 무한 스트림 (iterate) | Stream.iterate(0, n -> n + 1) | 초기값과 람다식을 이용해 연속된 값 생성 |
| 무한 스트림 (generate) | Stream.generate(Math::random) | 공급자 함수로 계속 값을 생성 |
| 파일 기반 | Files.lines(Path) | 텍스트 파일의 각 줄을 스트림으로 읽기 (IOException 처리 필요) |
2) 중간 연산
- 자주 사용되는 중간연산 메서드
| 메서드 | 설명 | 예시 |
| filter(Predicate) | 조건에 맞는 요소만 포함하는 스트림 반환 | .filter(s -> s.startsWith("A")) |
| map(Function) | 각 요소에 특정 함수를 적용해 변형한 스트림 반환 | .map(s -> s.toUpperCase()) |
| sorted() | 정렬 | .sorted() 또는 .sorted(Comparator.reverseOrder()) |
| distinct() | 중복 제거(hascode와 equals로 비교) | .distinct() |
| limit(n) | 앞에서 n개만 가져옴 | .limit(3) |
| skip(n) | 앞에서 n개 건너뜀 | .skip(2) |
| flatMap(Function) | 각 요소를 스트림으로 펼친 뒤 하나로 합침(중첩 제거) | .flatMap( s -> Arrays.stream(s.split(" "))) |
| peek(Consumer) | 스트림의 각 요소를 소비하면서 원본 스트림을 반환 (중간 결과값 찍어볼려고 디버깅용으로만 거의 씀.) |
.peek(x -> System.out.println(x)) |
3) 최종 연산
- 자주사용되는 최종연산 메서드
| 메서드 | 설명 | 예시 |
| forEachOrdered(Consumer) | 각 요소에 대해 순서대로 동작 수행(순서 보장) | .forEachOrdered(System.out::println) |
| collect(Collector) | 스트림 요소를 컬렉션/문자열 등으로 수집 | .collect(Collectors.toList()) |
| toArray() | 요소를 Object[]로 반환 | .toArray() |
| toArray(IntFunction<A[]>) | 원하는 타입 배열로 반환 | .toArray(String[]::new) |
| reduce(BinaryOperator) | 요소를 하나로 결합(집계)하여 반환 | .reduce(Integer::sum) |
| reduce(identity, BinaryOperator) | 초기값 포함해 집계 | .reduce(0, Integer::sum) |
| anyMatch(Predicate) | 하나라도 조건 만족하면 true | .anyMatch(n -> n > 5) |
| allMatch(Predicate) | 모두 조건 만족하면 true | .allMatch(n -> n > 5) |
| noneMatch(Predicate) | 모두 조건 불만족이면 true | .noneMatch(n -> n < 0) |
| findFirst() | 첫 번째 요소 반환 | .findFirst().orElse("없음") |
| findAny() | 임의의 요소 하나 반환 | .findAny().orElse("없음") |
| count() | 요소 개수 반환 | .count() |
| max(Comparator) | 최대 요소 반환 | .max(Integer::compareTo) |
| min(Comparator) | 최소 요소 반환 | .min(Integer::compareTo) |
| forEach(Consumer) | 각 요소에 대해 주어진 동작 수행 | .forEach(System.out::println) |
4. 스트림에서 Collectors 클래스
| 메서드 | 설명 | 예시 |
| toList() | 스트림 요소를 List로 수집 | stream.collect(Collectors.toList()) |
| toSet() | 스트림 요소를 Set으로 수집 | stream.collect(Collectors.toSet()) |
| toMap(keyMapper, valueMapper) | 키/값 매핑하여 Map 생성 | stream.collect(Collectors.toMap(String::length, s -> s)) |
| joining() | 문자열 연결 | stream.collect(Collectors.joining()) |
| joining(delimiter) | 구분자 포함 문자열 연결 | Collectors.joining(",") |
| joining(delimiter, prefix, suffix) | 접두사/접미사 포함 연결 | Collectors.joining(",", "[", "]") |
| counting() | 요소 개수 반환 | stream.collect(Collectors.counting()) |
| summarizingInt(mapper) | Int 통계 정보 반환 | Collectors.summarizingInt(String::length) |
| summarizingLong(mapper) | Long 통계 정보 반환 | Collectors.summarizingLong(User::getId) |
| summarizingDouble(mapper) | Double 통계 정보 반환 | Collectors.summarizingDouble(Product::getPrice) |
| averagingInt(mapper) | 평균(double) 반환 | Collectors.averagingInt(String::length) |
| averagingLong(mapper) | 평균(double) 반환 | Collectors.averagingLong(User::getId) |
| averagingDouble(mapper) | 평균(double) 반환 | Collectors.averagingDouble(Product::getPrice) |
| groupingBy(classifier) | 기준값으로 그룹화(Map<K, List>) | Collectors.groupingBy(String::length) |
| groupingBy(classifier, downstream) | 그룹화 후 추가 집계 | Collectors.groupingBy(String::length, Collectors.counting()) |
| partitioningBy(predicate) | true/false 기준 분할 | Collectors.partitioningBy(s -> s.length() > 3) |
| partitioningBy(predicate, downstream) | 분할 후 추가 집계 | Collectors.partitioningBy(s -> s.length()>3, Collectors.counting()) |
| reducing(BinaryOperator) | 요소를 하나로 결합(Optional 반환) | Collectors.reducing((a,b) -> a+b) |
| reducing(identity, BinaryOperator) | 초기값 포함 결합 | Collectors.reducing(0, Integer::sum) |
| reducing(identity, mapper, op) | 매핑 후 결합 | Collectors.reducing(0, String::length, Integer::sum) |
5. 직렬 스트림과 병렬 스트림
1) 직렬 스트림(Sequential Stream)
- 기본적으로 스트림은 직렬로 처리.
- 스트림의 각 요소가 순서대로 처리.
List<String> list = Arrays.asList("a", "b", "c");
list.stream().forEach(System.out::println);
2) 병렬 스트림(Parallel Stream)
- 병렬 스트림은 여러 스레드에서 스트림의 요소를 동시에 처리.
- 병렬 스트림은 큰 데이터셋을 처리할 때 유용하지만, 스레드간의 동기화 문제 발생 가능
List<String> list = Arrays.asList("a", "b", "c");
list.parallelStream().forEach(System.out::println);
| 구분 | 일반 스트림 (stream()) | 병렬 스트림 (parallelStream()) |
| 실행 방식 | 단일 쓰레드 | 멀티 쓰레드 (Fork/Join Pool) |
| 처리 순서 | 유지됨 | 보장되지 않음 (순서가 중요하면 위험) |
| 속도 | 느릴 수 있음 | 빠를 수 있음 (CPU 코어 많을수록) |
| 병렬 처리 | ❌ 안됨 | ✅ 자동 병렬 처리 |
| 사용 예 | 간단한 순차 처리 | CPU 코어 활용이 중요한 작업, 대용량 처리 |
'언어 > Java' 카테고리의 다른 글
| [Java] 주요 어노테이션 정리 - Java, Lombok, Spring, JPA (0) | 2026.03.03 |
|---|---|
| [Java] 어노테이션(Annotation)이란? 개념과 특징, 주요 어노테이션 정리 (0) | 2026.03.03 |
| [Java] 람다표현식(Lambda Expression) / 함수형 인터페이스 / 메서드 참조 연산(::) (0) | 2026.03.03 |
| [Java] 예외(Exception) - try catch문, throws, throw , 예외 종류 (0) | 2026.03.02 |
| [Java] 객체지향 프로그래밍 - 인터페이스(Interface)란? (0) | 2026.03.02 |
블로그의 정보
우와한개발자 님의 블로그
우와한개발자