Stream은 자바에서 배열이나 컬렉션(리스트, 셋 등)의 요소들을 함수형 스타일로 처리할 수 있게 해주는 기능이다.
즉, for문 대신 filter(), map(), collect() 같은 메서드들을 체인처럼 연결하여 데이터를 선언형으로 처리할 수 있다.
리스트나 콜렌션이 들어온다고 해도 원래 소스는 변경하지 않는다.
스트림을 만들 때는 Stream.Of(data)나 stream()으로 만들면 된다.
기존 방식:
List<String> names = Arrays.asList("Tom", "Jerry", "Anna");
List<String> filtered = new ArrayList<>();
for (String name : names) {
if (name.startsWith("A")) {
filtered.add(name.toUpperCase());
}
}
Stream 사용:
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());
✔ 코드가 훨씬 간결하고 가독성이 좋음
✔ 병렬 처리(parallel)도 쉽게 가능
✔ 불변성 유지 – 원본 데이터 변경 없음
Stream 연산은 크게 3단계로 나뉜다:
생성 (Create)
중간 연산 (Intermediate)
최종 연산 (Terminal)
| 메서드 | 설명 |
|---|---|
filter(Predicate) | 조건에 맞는 요소만 남김 |
map(Function) | 각 요소를 변형 |
sorted() | 정렬 |
limit(n) / skip(n) | 앞/뒤 요소를 자름 |
distinct() | 중복 제거 |
collect() | 결과를 리스트, 셋, 맵 등으로 수집 |
forEach() | 각 요소에 대해 작업 수행 |
count() | 요소 개수 반환 |
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = nums.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(result); // [4, 16]
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String result = names.stream()
.collect(Collectors.joining(", "));
System.out.println(result); // Alice, Bob, Charlie
class User {
String name;
int age;
// 생성자 & getter
}
List<User> users = Arrays.asList(
new User("Tom", 25),
new User("Jerry", 30)
);
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
Stream은 1회용: 한 번 소비되면 다시 사용할 수 없다.
지연 연산(Lazy Evaluation): 최종 연산이 호출되기 전까지는 실제로 수행되지 않는다.
성능에 민감한 경우: 너무 많은 중간 연산은 성능에 영향을 줄 수 있다.
Java Stream을 활용하면 코드가 간결해지고, 가독성도 좋아지며, 데이터 처리 효율성까지 향상된다.