⚙️ 스트림이란?
- 데이터(컬렉션, 배열 등)의 요소들을 연속적으로 처리할 수 있는 추상화된 흐름
🔁 스트림의 특징
| 특징 | 설명 |
|---|---|
| 선언형 | 반복문 없이 무엇을 할지 선언적으로 표현 |
| 함수형 프로그래밍 지원 | 람다와 함께 쓰며 간결한 코드 작성 가능 |
| 중간/최종 연산 분리 | 연산을 체이닝하면서 최종 연산 전까지 실제 실행 X (지연 평가) |
| 내부 반복 | 개발자가 반복을 구현하지 않고 스트림이 대신 처리 |
| 불변성 | 스트림을 사용해도 원래의 컬렉션은 변경되지 않음 |
✅ 스트림의 기본 구조
list.stream() // 🔹 스트림 생성
.filter(x -> x > 0) // 🔸 중간 연산 (조건 필터링
.map(x -> x * 2) // 🔸 중간 연산 (값 변경)
.forEach(System.out::println); // 🔸 최종 연산 (출력)
🧵 스트림을 사용하여 각 요소를 10배로 변환 후, 리스트로 변환하는 예제
stream() → map() → collect() 순으로 데이터 흐름 처리stream(): 데이터 준비 - 데이터를 스트림으로 변환하여 연산 흐름을 만들 준비를 함map(): 중간 연산 등록 - 각 요소를 주어진 함수에 적용해서 변환collect(): 최종 연산 - 결과를 원하는 형태(List, Set)로 수집🗒️ 예시 코드:
// 1. 데이터 준비: 스트림 생성
Stream<Integer> stream = arrayList.stream();
// 2. 중간 연산 등록: 각 요소를 10배로 변환 로직 등록
Stream<Integer> mappedStream = stream.map(num -> num * 10);
// 3. 최종 연산: 최종 결과 리스트로 변환
List<Integer> ret2 = mappedStream.collect(Collectors.toList());
// ✅ 한 줄로 표현 가능
List<Integer> ret2 = arrayList.stream() // 1. 데이터 준비
.map(num -> num * 10) // 2. 중간 연산 등록
.collect(Collectors.toList()); // 3. 최종 연산
⚠️ 스트림 사용 시 주의 사항
| 항목 | 설명 |
|---|---|
| 스트림은 1회용 | 한 번 사용하면 재사용 불가 ❌ |
| 중간 연산만으론 실행 안 됨 | 최종 연산이 있어야 실행됨 |
순서를 보장해야 한다면 forEachOrdered() 사용 | 병렬 스트림에서 유용 |
| 원본 컬렉션은 변하지 않음 | 스트림은 불변 데이터 처리에 적합 |
📝 스트림과 람다식 활용
- 스트림과 람다식을 함께 사용해서
각 요소 * 10예시 작성 →map()활용
1. 익명 클래스를 변수에 담아 활용
public class Main {
public static void main(String[] args) {
// ArrayList 선언
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
// ✅ 1. 익명클래스를 변수에 담아 활용
Function<Integer, Integer> function = new Function<>() {
@Override
public Integer apply(Integer integer) {
return integer * 10;
}
};
List<Integer> ret3 = arrayList.stream()
.map(function)
.collect(Collectors.toList());
System.out.println("ret3 = " + ret3);
}
}
2. 람다식을 변수로 활용
public class Main {
public static void main(String[] args) {
// ArrayList 선언
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
// ✅ 2. 람다식을 변수에 담아 활용
Function<Integer, Integer> functionLambda = (num -> num * 10);
List<Integer> ret4 = arrayList.stream()
.map(functionLambda)
.collect(Collectors.toList());
System.out.println("ret4 = " + ret4);
}
}
3. 람다식을 매개변수에 직접 활용
public class Main {
public static void main(String[] args) {
// ArrayList 선언
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
// ✅ 3. 람다식을 직접 활용
List<Integer> ret5 = arrayList.stream()
.map(num -> num * 10)
.collect(Collectors.toList());
System.out.println("ret5 = " + ret5);
}
}
✏️ 연습 문제) filter를 람다식으로 활용해 보기
- 익명 클래스 활용
- 람다식 만들기
- 직접 람다식 대입
package chapter3_6;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
// 1. 익명 클래스 활용
Predicate<Integer> predicate = new Predicate<>() {
@Override
public boolean test(Integer integer) {
return (integer % 2 == 0);
}
};
// 2. 람다식 만들기
Predicate<Integer> filterLambda = (num -> num % 2 == 0);
// 3.직접 람다식 대입
List<Integer> ret3 = arrayList.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
List<Integer> ret1 = arrayList.stream().filter(predicate).collect(Collectors.toList());
List<Integer> ret2 = arrayList.stream().filter(filterLambda).collect(Collectors.toList());
System.out.println("ret1 = " + ret1);
System.out.println("ret2 = " + ret2);
System.out.println("ret3 = " + ret3);
}
}