Java 8 이후로 도입된 Stream API는 Java 컬렉션을 다루는 방식에 큰 변화를 가져왔습니다. 복잡한 for문을 대체하고, 더 선언적이고 간결한 코드를 작성할 수 있게 도와주죠. 이번 글에서는 실무에서 자주 사용되는 Stream 패턴을 실제 예제와 함께 살펴보겠습니다.
Stream은 크게 세 가지 요소로 이루어져 있습니다:
Source: 데이터를 생성하는 컬렉션, 배열, I/O 채널 등
Intermediate Operation: 중간 연산으로, filter, map, sorted 등의 연산을 연결할 수 있습니다.
Terminal Operation: 최종 연산으로, collect, forEach, reduce 등이 있습니다.
1. 리스트 필터링 (Filtering)
예제: 특정 조건을 만족하는 객체 필터링
List<User> users = List.of(
newUser("Alice", 25),
newUser("Bob", 30),
newUser("Charlie", 19)
);
List<User> adults = users.stream()
.filter(user -> user.getAge() >= 20)
.collect(Collectors.toList());
// 결과: Alice, Bob
filter는 조건에 맞는 요소만 걸러내는 데 유용합니다. 데이터 유효성 검사, 검색 기능 등에 많이 활용됩니다.
2. 리스트 매핑 (Mapping)
예제: 객체 리스트에서 특정 필드만 추출
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// 결과: ["Alice", "Bob", "Charlie"]
map은 객체를 다른 형태로 변환할 때 사용합니다. DTO 변환, 이름만 추출 등 다양한 작업에 활용할 수 있습니다.
3. 정렬 (Sorting)
예제: 나이 기준 오름차순 정렬
List<User> sortedByAge = users.stream()
.sorted(Comparator.comparingInt(User::getAge))
.collect(Collectors.toList());
sorted는 Comparator와 함께 쓰여 컬렉션을 정렬할 수 있게 해줍니다. 역순 정렬을 원한다면
flatMap은 여러 리스트를 하나로 펼치는 데 유용합니다. JSON 데이터 파싱, CSV 필드 분리, 중첩 리스트 병합에 자주 사용됩니다.
마무리
Stream API는 자바 개발자가 좀 더 함수형 프로그래밍 스타일로 사고하고 코딩할 수 있도록 도와줍니다. 위의 패턴들은 실무에서 매우 자주 사용되며, 익숙해진다면 코드의 가독성과 유지보수성이 크게 향상됩니다.
특히, Stream은 컬렉션 처리뿐 아니라 API 응답 데이터 가공, 대용량 데이터 필터링, 통계 분석 등 다양한 상황에서 강력한 도구가 됩니다. 그러나 너무 많은 연산을 체이닝하거나 복잡한 로직을 담으면 가독성이 떨어질 수 있으니 적절한 밸런스를 유지하는 것이 중요합니다.
다음 글에서는 Stream 성능 팁이나 병렬 스트림 (parallel stream) 사용 시 주의사항 등 좀 더 심화된 주제를 다뤄보겠습니다.
참고 클래스
classUser {
private String name;
privateint age;
private List<String> hobbies;
publicUser(String name, int age) {
this.name = name;
this.age = age;
}
publicUser(String name, List<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
public String getName() { return name; }
publicintgetAge() { return age; }
public List<String> getHobbies() { return hobbies; }
댓글