The Java Stream API
11 Mar 2025
1. Java Stream 함수의 도입 배경
Java는 전통적으로 컬렉션(예: List, Set, Map)을 다룰 때 반복문(for 또는 while)을 사용하여 데이터를 처리하는 방식이 일반적이었다. 하지만 이러한 방식은 코드의 가독성이 떨어지고, 병렬 처리(Parallel Processing)를 지원하기 어렵다는 한계가 있었다.
이를 해결하기 위해 Java 8(2014년 출시) 에서 Stream API가 도입되었다.
Stream API는 함수형 프로그래밍(Functional Programming) 패러다임을 지원하며, 컬렉션 데이터를 보다 직관적이고 효율적으로 처리할 수 있도록 도와준다.
2. Java Stream의 주요 개념
✅ 스트림(Stream)이란?
스트림은 데이터의 흐름을 추상화한 객체로, 컬렉션 데이터를 선언적(Declarative) 방식으로 처리할 수 있도록 해준다.
스트림은 다음과 같은 특징을 가진다.
스트림은 다음과 같은 특징을 가진다.
- 데이터를 변경하지 않음: 기존 컬렉션을 변경하지 않고, 새로운 데이터를 생성하여 반환한다.
- 한 번만 사용 가능: 스트림은 일회용으로 사용되며, 한 번 처리하면 재사용할 수 없다.
- 지연 연산(Lazy Evaluation): 필요할 때만 실행되므로 성능 최적화가 가능하다.
- 병렬 처리 가능: parallelStream()을 사용하면 손쉽게 병렬 처리할 수 있다.
✅ 스트림 연산 종류
스트림은 크게 중간 연산(Intermediate Operation) 과 최종 연산(Terminal Operation) 으로 나뉜다.
연산 종류 | 설명 | 예시 |
---|---|---|
중간 연산 | 데이터를 가공하는 과정 (스트림을 반환함) | filter(), map(), sorted() |
최종 연산 | 결과를 도출하는 과정 (스트림을 소모함) | forEach(), collect(), count() |
3. Java Stream 샘플 코드
📌 기본적인 Stream 사용 예제
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 1. 이름이 'B'로 시작하는 요소만 필터링하고 출력
names.stream()
.filter(name -> name.startsWith("B"))
.forEach(System.out::println);
}
}
출력 결과
Bob
📌 map()을 활용한 데이터 변환
map()은 각 요소를 다른 형태로 변환할 때 사용한다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamMapExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 모든 이름을 대문자로 변환
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseNames);
}
}
출력 결과
[ALICE, BOB, CHARLIE, DAVID]
📌 sorted()를 활용한 정렬
import java.util.Arrays;
import java.util.List;
public class StreamSortedExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 3);
// 오름차순 정렬
numbers.stream()
.sorted()
.forEach(System.out::println);
}
}
출력결과
1
2
3
5
8
📌 reduce()를 활용한 누적 연산
reduce()는 값을 하나로 합치는 연산을 수행한다.
import java.util.Arrays;
import java.util.List;
public class StreamReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 모든 숫자의 합 구하기
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("합계: " + sum);
}
}
출력결과
합계: 15
📌 latMap()을 활용한 예제
flatMap()은 스트림 내부의 요소를 단일 스트림으로 변환할 때 사용됩니다.
특히 리스트의 리스트(List of Lists) 또는 문자열을 개별 요소로 변환하는 데 유용합니다.
1️⃣ List<List >을 단일 List 으로 변환
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("Apple", "Banana"),
Arrays.asList("Orange", "Grapes"),
Arrays.asList("Mango", "Peach")
);
// flatMap()을 사용하여 중첩된 리스트를 평탄화
List<String> flatList = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flatList);
}
}
출력결과
[Apple, Banana, Orange, Grapes, Mango, Peach]
2️⃣ 문자열 리스트를 단일 문자(Character) 리스트로 변환
아래 예제에서는 "Hello"와 "World"를 개별 문자 리스트로 변환합니다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapCharExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("Hello", "World");
// 문자열을 문자(Character) 단위로 변환
List<Character> characterList = words.stream()
.flatMap(word -> word.chars().mapToObj(c -> (char) c))
.collect(Collectors.toList());
System.out.println(characterList);
}
}
출력 결과
[H, e, l, l, o, W, o, r, l, d]
✅ flatMap()은 중첩된 데이터를 펼쳐서 하나의 리스트로 변환할 때 강력한 기능을 제공합니다! 🚀
📌 병렬 스트림(Parallel Stream) 활용
parallelStream()을 사용하면 데이터를 병렬로 처리할 수 있다.
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
names.parallelStream()
.forEach(System.out::println);
}
}
출력 결과 (실행할 때마다 순서가 다름)
Bob
David
Charlie
Alice
4. 결론
Java의 Stream API는 기존 반복문보다 더 간결하고 가독성이 높은 코드를 작성할 수 있도록 도와준다.
또한 병렬 처리 기능을 활용하면 성능 최적화도 가능하다.
또한 병렬 처리 기능을 활용하면 성능 최적화도 가능하다.
💡 핵심 정리
- Stream API는 Java 8부터 도입됨.
- 컬렉션 데이터를 선언적 방식으로 처리할 수 있음.
- 중간 연산(filter(), map(), sorted())과 최종 연산(forEach(), collect(), reduce())을 활용.
- parallelStream()을 통해 병렬 처리 가능.
Java 8 이후부터는 Stream API를 적극적으로 활용하여 더 효율적인 코드를 작성해보는 것이 좋다! 🚀