학습
Java 스트림 API
Java 스트림 API — 중급
스트림 API
1. 도입 (왜 배워야 하는가)
Java의 스트림 API는 데이터의 집합을 처리하는 새로운 방법을 제공합니다. 데이터 처리 작업이 복잡해지면서, 매번 반복문을 사용해 데이터를 필터링하고 변형하는 전통적인 방법은 가독성과 유지보수성 측면에서 문제가 발생할 수 있습니다. 스트림 API를 사용하면 이러한 작업을 보다 직관적이고 선언적으로 할 수 있어 코드가 간결해지고 이해하기 쉬워집니다.
만약 스트림 API를 모른다면, 데이터 처리 시 매번 반복문을 작성해야 하므로 코드가 장황해지고, 버그가 발생하기 쉬워집니다. 또한, 병렬 처리와 같은 성능 최적화를 놓칠 수 있습니다. 따라서, 현대 Java 프로그래밍에서 스트림 API는 필수적인 도구입니다.
2. 핵심 개념 설명
- 스트림(Stream): 데이터의 연속적인 흐름을 표현합니다. 스트림은 데이터를 처리하는 방법을 제공하지만, 데이터를 저장하지는 않습니다.
- 중간 연산(Intermediate Operation): 스트림을 변형하지만 결과를 즉시 반환하지 않는 연산입니다. 예를 들어,
filter,map등이 있습니다. - 최종 연산(Terminal Operation): 스트림을 소비하고 결과를 반환하는 연산입니다. 예를 들어,
forEach,collect등이 있습니다.
| 용어 | 설명 |
|---|---|
| 스트림 | 데이터의 흐름을 나타내며, 데이터 처리 작업을 수행합니다. |
| 중간 연산 | 스트림을 변형하며, 결과를 즉시 반환하지 않습니다. |
| 최종 연산 | 스트림을 소비하고, 최종적인 결과를 반환합니다. |
3. 기본 코드 예제
가장 기본적인 스트림 API 사용법을 보여주는 코드입니다.
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 스트림을 생성하고 짝수만 필터링하여 출력
numbers.stream()
.filter(n -> n % 2 == 0) // 짝수만 필터링
.forEach(System.out::println); // 결과 출력
}
}
실행 결과
2
4
코드 설명
numbers리스트를 생성합니다:1, 2, 3, 4, 5라는 정수 리스트입니다.numbers.stream()메서드를 호출하여 스트림을 생성합니다.filter(n -> n % 2 == 0)는 짝수만 필터링하는 중간 연산입니다.forEach(System.out::println)은 최종 연산으로, 필터링된 결과를 출력합니다.- 스트림 API는 데이터 처리를 더 직관적이고 선언적으로 합니다.
- 중간 연산과 최종 연산의 개념을 이해하고 활용할 수 있어야 합니다.
- 실무에서는 필터링, 매핑, 집계 등의 패턴을 자주 사용합니다.
- 스트림은 한 번만 사용할 수 있다는 점을 유의해야 합니다.
4. 실전 활용 예제
예제 1: 쇼핑몰에서 장바구니 구현
고객의 장바구니에 담긴 상품에서 가격이 10000원 이상의 상품만 필터링하고, 총합을 계산하는 예제입니다.
import java.util.Arrays;
import java.util.List;
public class ShoppingCart {
public static void main(String[] args) {
List<Integer> prices = Arrays.asList(5000, 15000, 20000, 3000);
int total = prices.stream()
.filter(price -> price >= 10000) // 10000원 이상 필터링
.mapToInt(Integer::intValue) // int로 변환
.sum(); // 합계 계산
System.out.println("총 합계: " + total); // 결과 출력
}
}
실행 결과
총 합계: 35000
예제 2: 회원 리스트에서 성인 회원 필터링
회원 리스트에서 성인(18세 이상) 회원의 이름을 출력하는 예제입니다.
import java.util.Arrays;
import java.util.List;
class Member {
String name;
int age;
Member(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class AdultMembers {
public static void main(String[] args) {
List<Member> members = Arrays.asList(
new Member("Alice", 20),
new Member("Bob", 17),
new Member("Charlie", 25)
);
members.stream()
.filter(member -> member.getAge() >= 18) // 성인 필터링
.map(Member::getName) // 이름 추출
.forEach(System.out::println); // 결과 출력
}
}
실행 결과
Alice
Charlie
5. 흔한 실수와 주의사항
실수 1: 중간 연산과 최종 연산의 혼동
// 잘못된 코드
List<Integer> result = numbers.stream().filter(n -> n % 2 == 0); // 최종 연산이 없음
올바른 코드:
List<Integer> result = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());
최종 연산인 collect를 사용해야 결과를 얻을 수 있습니다.
실수 2: 스트림 재사용
스트림은 한 번만 사용 가능합니다.
Stream<Integer> stream = numbers.stream();
stream.filter(n -> n % 2 == 0).forEach(System.out::println);
stream.filter(n -> n < 5).forEach(System.out::println); // IllegalStateException 발생
올바른 방법은 매번 새 스트림을 생성하는 것입니다.
6. 정리
다음에 학습하면 좋은 주제로는 람다 표현식, 병렬 스트림 및 Optional 클래스가 있습니다. 이들은 스트림 API와 함께 활용할 때 더 큰 효과를 발휘합니다.