Stream(Stream)
데이터를 효율적이고 선언적으로 처리할 수 있게 해주는 API이다. 컬렉션(List, Set 등)의 데이터를 필터링, 매핑, 정렬 등의 작업을 함수형 프로그래밍(순수 함수를 조합하고 소프트웨어를 만드는 방식) 스타일로 처리할 수 있게 도와준다. 쉽게 말해서, 데이터를 효율적으로 흐름에 맞추어 처리한다는 의미이다.
스트림의 특징
- 스트림은 데이터를 저장하지 않는다.
- 원본 데이터를 변경하지 않는다. (불변성 유지)
- 스트림은 컬렉션(List, Set 등)과 함께 자주 활용된다.
- 생성 → 중간 연산(Intermediate Operation) → 최종 연산(Terminal Operation) 순으로 처리 된다. (선언형 스타일)
- 지연 연산(Lazy Evaluation)을 사용하여 최종 연산이 수행될 때까지 중간 연산이 실제로 실행되지 않는다.
스트림의 구성 요소
1. 생성 (Stream 생성)
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream = list.stream();
2. 중간 연산 (Intermediate Operation)
stream.filter(s -> s.length() > 5);
- 스트림을 변형하거나 필터링한다.
- 예: filter(), map(), sorted(), distinct() 등
3. 최종 연산 (Terminal Operation)
long count = list.stream()
.filter(s -> s.length() > 5)
.count();
- 결과를 도출하고 스트림을 종료한다.
- 예: forEach(), collect(), count(), reduce() 등
스트림 단계에 따른 주요 메서드 정리
단계 | 메서드 | 설명 |
1. 데이터 준비 | stream() | 데이터를 스트림으로 변환 |
parallelStream() | 데이터를 스트림으로 변환(병렬 처리, 멀티 스레드) | |
2. 중간 연산 등록 (즉시 실행되지 않는다.) |
filter(Predicate) | 조건에 맞는 요소만 필터링 |
map(Function) | 요소를 다른 형태로 매핑 (예: 대문자로 변환) | |
sorted() | 정렬 | |
distinct() | 중복 제거 | |
limit(n) | 앞에서부터 n개 요소 제한 | |
3. 최종 연산 | collect() | 스트림 결과를 리스트나 집합 등으로 수집 |
reduce() | 모든 요소를 하나로 합침 | |
forEach() | 각 요소에 대해 동작 수행 | |
count() | 총 요소 갯수 반환 |
비교해보기 (for vs 스트림)
각 요소를 10배로 변환 후 출력하는 예시로 알아봅시다.
- ArrayList의 각 요소는 10배로 변환한다.
- 아래 예시를 보고 for 문과 스트림을 비교해보자.
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
// for 명령형 스타일 : 각 요소 * 10 처리
ArrayList<Object> result1 = new ArrayList<>();
for (Integer num : arrayList) {
Integer nultipliedNum = num * 10;
result1.add(nultipliedNum);
}
System.out.println("result1 = " + result1);
// stream 선언형 스타일: 각 요소 * 10 처리
List<Integer>result2 = arrayList.stream() // 1. 데이터 흐름 준비
.map(num -> num * 10) // 2. 중간 연산 등록
.collect(Collectors.toList()); // 3. 최종 연산 단계
System.out.println("result2 = " + result2);
}
}
for문은 5줄에 걸쳐서 진행되었지만, stream을 사용하여 한 줄로 가능하게 되었다.
익명 클래스와 람다를 스트림에서 활용
다음 코드에서 map 매개 변수에서 람다 식이 활용되었다. 어떻게 활용할 수 있었는지 뜯어보자.
List<Integer>result2 = arrayList.stream() // 1. 데이터 흐름 준비
.map(num -> num * 10) // 2. 중간 연산 등록
.collect(Collectors.toList()); // 3. 최종 연산 단계
System.out.println("result2 = " + result2);
우선 익명 클래스와 람다를 활용하려면, map 매개변수의 인터페이스가 추상 메서드를 하나만 가지고 있어야 한다.
map 매개 변수인 mapper의 인터페이스를 확인해보니 함수형 인터페이스이다.
함수형 인터페이스는 추상 메서드가 하나이므로, 익명 클래스와 람다를 사용할 수 있다.
사용되는 과정
1. 익명 클래스를 직접 만들어서 변수에 담아 매개 변수로 전달하는 방법
Function<Integer, Integer> function = new Function<>() {
@Override
public Integer apply(Integer integer) {
return integer * 10;
}
};
List<Integer>result3 = arrayList.stream() // 1. 데이터 흐름 준비
.map(function) // 2. 중간 연산 등록
.collect(Collectors.toList()); // 3. 최종 연산 단계
System.out.println("result3 = " + result3);
2. 람다식을 만들어서 변수에 담아 매개변수로 전달
Function<Integer, Integer> functionalLambda = (interger -> interger * 10);
List<Integer>result4 = arrayList.stream() // 1. 데이터 흐름 준비
.map(functionalLambda) // 2. 중간 연산 등록
.collect(Collectors.toList()); // 3. 최종 연산 단계
System.out.println("result4 = " + result4);
3. 람다식을 직접 매개변수로 전다
// 3. 람다식을 직접 매개변수로 전달
List<Integer>result5 = arrayList.stream() // 1. 데이터 흐름 준비
.map(num -> num * 10) // 2. 중간 연산 등록
.collect(Collectors.toList()); // 3. 최종 연산 단계
System.out.println("result5 = " + result5);
https://kimyongjun0129.tistory.com/106
[Java] 람다(Lamda)란?
람다(Lamda)익명 클래스를 간단하게 표현하는 방식이다. 즉, 익명 클래스의 메서드를 코드 블록으로 만들어서 변수에 넘기거나 인자로 전달 할 수 있게 해주는 기능이다. 람다 문법Calculator c = (a, b
kimyongjun0129.tistory.com
'JAVA' 카테고리의 다른 글
[Java] Lv_2 계산기 프로젝트 (0) | 2025.04.18 |
---|---|
[Java] Lv_1 계산기 프로젝트 (0) | 2025.04.17 |
[Java] 익명 클래스란? (0) | 2025.04.17 |
[Java] 람다(Lamda) (0) | 2025.04.17 |
[Java] 추상 클래스(abstract class)란? (0) | 2025.04.16 |