728x90
스트림(Stream)은 데이터를 작은 조각으로 나누어 순차적으로 처리하는 방식이다.
예를 들어, 유튜브를 생각해보자.
우리가 영상을 볼 때, 처음부터 끝까지 파일을 한 번에 받아오는 게 아니라, 작은 단위(조각)로 조금씩 받아오면서 영상이 재생된다.
만약 중간에 분 단위로 스킵하면 영상이 잠깐 멈추는(버퍼링) 이유는, 해당 구간의 조각 데이터를 아직 못 받았기 때문이다.
이처럼 스트리밍은 전체 데이터를 한꺼번에 가져오지 않아도 실시간으로 처리할 수 있어서, 메모리도 효율적으로 사용할 수 있다.
⚙️ 스트림의 특징
- 원본 데이터를 변경하지 않는다
- 스트림은 원본 데이터를 건드리지 않고, 복사된 데이터를 기준으로 작업한다.
- 휘발성이다
- 스트림은 한 번 사용하면 다시 사용할 수 없다.
(재사용하려면 새 스트림을 만들어야 함)
- 스트림은 한 번 사용하면 다시 사용할 수 없다.
- 컬렉션에서 스트림을 만들 수 있다
- 리스트, 배열 등 다양한 컬렉션은 .stream()이나 Stream.of()로 쉽게 스트림을 생성할 수 있다.
Stream은 각 단계('생성 -> 가공 -> 결과')를 거친 후 생성된다.
📌 스트림의 처리 단계
1. 생성
Collection의 Stream 생성
앞서 말했듯이, stream은 collection에 포함되어 있어서 점(.)을 사용하여 손쉽게 호출이 가능하다.
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
Stream<String> listStream = list.stream();
배열의 Stream 생성
배열은 Stream.of를 사용하여 생성한다.
String[] fruits = {"Apple", "Banana", "Orange"};
Stream<String> stringStream = Stream.of(fruits);
2. 가공 (중간 연산)
중간연산을 사용하여 원하는 결과를 출력할 수 있도록 유도한다.
중간연산은 함수형 인터페이스를 사용하며 여러 중간연산을 연결시켜 사용하는 메서드 체이닝이 가능하다.
✅ filter() - 조건 걸러내기
Stream<String> stream =
fruits.stream()
.filter(name -> name.contains("Apple"));
✅ map() - 데이터 변환
Stream<String> stream =
fruits.stream()
.map(s -> s.toUpperCase());
✅Sorted() - 정렬
// 오름차순 정렬
Stream<String> stream = fruits.stream().sorted();
// 내림차순 정렬
Stream<String> stream = fruits.stream().sorted(Comparator.reverseOrder());
✅ distinct() – 중복 제거
Stream<String> stream = fruits.stream().distinct();
3. 결과 (최종 연산)
✅ 값 계산하기 (min, max, sum, count 등)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 총합
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
System.out.println("합: " + sum);
// ....
// 포함된 요소의 갯수
long count = numbers.stream().count();
System.out.println("갯수: " + count);
✅ collect() – 결과 수집
🔹 리스트로 수집
List<String> result = fruits.stream().collect(Collectors.toList());
🔹 맵으로 수집
Map<String, Integer> map = fruits.stream()
.collect(Collectors.toMap(Fruit::getName, Fruit::getPrice));
🔹 문자열로 결합 (joining())
String result = fruits.stream()
.map(Fruit::getName)
.collect(Collectors.joining(", ", "<", ">"));
🔹 합계, 평균 등
int total = fruits.stream().collect(Collectors.summingInt(Fruit::getPrice));
double avg = fruits.stream().collect(Collectors.averagingInt(Fruit::getPrice));
🔹 요약 통계 (summarizingInt())
IntSummaryStatistics stats = fruits.stream()
.collect(Collectors.summarizingInt(Fruit::getPrice));
✅ groupingBy() – 그룹으로 묶기
Map<Integer, List<Fruit>> grouped = fruits.stream()
.collect(Collectors.groupingBy(Fruit::getPrice));
✅ partitioningBy() – 조건에 따라 두 그룹으로 나누기
Map<Boolean, List<Fruit>> partitioned = fruits.stream()
.collect(Collectors.partitioningBy(fruit -> fruit.getPrice() > 1000));
✅ match() – 조건 만족 여부 확인
List<String> names = Arrays.asList("Eric", "Elena", "Java");
boolean any = names.stream().anyMatch(name -> name.contains("a"));
boolean all = names.stream().allMatch(name -> name.length() > 3);
boolean none = names.stream().noneMatch(name -> name.endsWith("s"));
✅ forEach() – 하나씩 처리
names.stream().forEach(System.out::println);
'Java' 카테고리의 다른 글
Servlet, JSP (0) | 2025.04.02 |
---|---|
Comparable VS Comparator (0) | 2025.03.02 |
오버로딩(Overloading)과 오버라이딩(Overriding) (0) | 2023.06.09 |
모던 자바 알아보기(람다, 스트림, Optional) (0) | 2023.05.30 |
쓰레드의 상태 (0) | 2023.05.30 |