Java 8에서 도입된 Stream API는 컬렉션 데이터를 함수형 프로그래밍 방식으로 처리할 수 있게 해주는 강력한 도구입니다. 이번 글에서는 Stream API의 핵심 개념부터 실전 활용까지 상세히 알아보겠습니다.
1. Stream API 기초 개념 📚
Stream은 데이터의 흐름을 표현하는 객체로, 컬렉션의 요소들을 람다식을 이용해 효과적으로 처리할 수 있게 해줍니다.
List<String> names = Arrays.asList("John", "Jane", "Kim", "Park", "Lee");
// 기존 방식
List<String> filteredNames = new ArrayList<>();
for (String name : names) {
if (name.length() <= 3) {
filteredNames.add(name.toUpperCase());
}
}
// Stream API 활용
List<String> streamFilteredNames = names.stream()
.filter(name -> name.length() <= 3)
.map(String::toUpperCase)
.collect(Collectors.toList());
2. Stream 연산의 종류 🔄
2.1 중간 연산 (Intermediate Operations)
public class StreamIntermediateDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers.stream()
.filter(n -> n % 2 == 0) // 짝수 필터링
.map(n -> n * 2) // 값 변환
.sorted() // 정렬
.distinct() // 중복 제거
.peek(System.out::println) // 중간 결과 확인
.collect(Collectors.toList());
}
}
2.2 최종 연산 (Terminal Operations)
public class StreamTerminalDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 다양한 최종 연산 예시
long count = numbers.stream().count();
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
boolean allMatch = numbers.stream().allMatch(n -> n > 0);
boolean anyMatch = numbers.stream().anyMatch(n -> n % 2 == 0);
Optional<Integer> find = numbers.stream().findFirst();
// collect 연산
List<Integer> collected = numbers.stream()
.collect(Collectors.toList());
}
}
3. 실전 활용 예제 💡
3.1 객체 스트림 처리
public class Person {
private String name;
private int age;
private String city;
// 생성자, getter, setter 생략
}
public class PersonStreamExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("John", 25, "New York"),
new Person("Jane", 30, "London"),
new Person("Kim", 35, "Seoul")
);
// 나이가 30 이상인 사람들의 이름을 알파벳 순으로 정렬
List<String> filteredNames = people.stream()
.filter(p -> p.getAge() >= 30)
.map(Person::getName)
.sorted()
.collect(Collectors.toList());
}
}
3.2 그룹화와 집계
public class StreamGroupingExample {
public static void main(String[] args) {
List<Person> people = getPeople(); // 사람 목록 가져오기
// 도시별 평균 나이 계산
Map<String, Double> avgAgeByCity = people.stream()
.collect(Collectors.groupingBy(
Person::getCity,
Collectors.averagingInt(Person::getAge)
));
// 도시별 인원 수 계산
Map<String, Long> countByCity = people.stream()
.collect(Collectors.groupingBy(
Person::getCity,
Collectors.counting()
));
}
}
4. 병렬 스트림 (Parallel Streams) ⚡
public class ParallelStreamDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 순차 스트림
long startTime = System.nanoTime();
numbers.stream()
.map(n -> performHeavyComputation(n))
.collect(Collectors.toList());
long endTime = System.nanoTime();
// 병렬 스트림
long parallelStartTime = System.nanoTime();
numbers.parallelStream()
.map(n -> performHeavyComputation(n))
.collect(Collectors.toList());
long parallelEndTime = System.nanoTime();
// 성능 비교 출력
System.out.println("순차 처리 시간: " + (endTime - startTime));
System.out.println("병렬 처리 시간: " + (parallelEndTime - parallelStartTime));
}
}
5. Stream API 성능 최적화 팁 🚀
5.1 적절한 스트림 선택
public class StreamOptimizationTips {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 작은 데이터셋: 순차 스트림 사용
numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 큰 데이터셋: 병렬 스트림 고려
numbers.parallelStream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
}
}
5.2 Short-circuiting 활용
public class ShortCircuitExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// findFirst()를 사용한 조기 종료
Optional<Integer> firstEven = numbers.stream()
.filter(n -> n % 2 == 0)
.findFirst();
// anyMatch()를 사용한 조기 종료
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
}
}
결론 📝
Stream API는 Java에서 데이터 처리를 위한 강력하고 유연한 도구입니다. 주요 이점은:
- 간결하고 가독성 높은 코드 작성 가능
- 함수형 프로그래밍 스타일 지원
- 병렬 처리를 통한 성능 최적화
- 풍부한 중간/최종 연산 제공
실무 적용 시 고려사항 ⚠️
- 상황에 따른 적절한 스트림 선택 (순차/병렬)
- 성능에 민감한 경우 벤치마킹 필수
- 가독성과 유지보수성 고려
- Short-circuiting 연산 활용
Stream API를 효과적으로 활용하면 더 깔끔하고 효율적인 코드를 작성할 수 있습니다. 실제 프로젝트에서 적절히 활용하여 코드 품질을 향상시키시기 바랍니다.
'개발일지' 카테고리의 다른 글
Java Records: 현대적 데이터 클래스의 새로운 패러다임 📝 (0) | 2024.12.10 |
---|---|
Java Optional: 우아한 null 처리의 완벽 가이드 ✨ (0) | 2024.12.10 |
Java String Pool의 내부 동작 원리: 메모리 최적화와 문자열 관리 심층 분석 📘 (0) | 2024.12.10 |
JavaScript Set과 Map: 데이터를 효율적으로 다루는 방법 (0) | 2024.12.02 |
자바스크립트 함수 완벽 가이드: 함수 표현식부터 모던 자바스크립트까지 (0) | 2024.11.30 |