일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 공유락
- 개발자기술면접
- 객체지향언어
- 백엔드면접
- http
- jpa
- Application
- 네트워크
- equals
- 운영체제
- 자바
- 배타락
- 알고리즘
- 자바8
- 면접준비
- 개발자면접
- 백엔드
- java
- hashcode
- 기술면접대비
- 기술면접
- 자바면접
- DB
- 스트림
- 자바기술면접
- 데이터베이스
- stream
- Spring
- 스프링
- lock
- Today
- Total
서은파파의 추월차선
Java Stream API에 대해 알아보기 본문
Java Stream API 정리
Java Stream API는 Java 8에 도입된 기능으로, 컬렉션 데이터를 처리하는 데 있어 선언적이고 함수형 프로그래밍 스타일을 제공합니다. 이 포스팅에서는 Java Stream API의 이론, 사용법, 예시, 장단점에 대해 자세히 정리하겠습니다.
1. 이론
1.1 Stream의 정의
Stream은 데이터의 흐름을 추상화한 것으로, 컬렉션, 배열, 또는 I/O 채널 등의 데이터 소스를 처리하는 데 사용됩니다. Stream은 데이터 원본을 변경하지 않고, 필요한 연산을 체이닝 방식으로 선언적으로 기술할 수 있습니다.
1.2 Stream의 특징
- 선언적 방식: 데이터 처리 과정을 간결하고 가독성 있게 표현.
- Lazy Evaluation(지연 연산): 최종 연산이 호출되기 전까지 중간 연산은 실행되지 않음.
- 파이프라인 처리: 중간 연산을 체이닝하여 데이터를 단계적으로 처리.
- 불변성: 원본 데이터를 변경하지 않음.
- 병렬 처리 지원: parallelStream()을 사용하여 멀티코어를 활용한 병렬 연산 가능.
1.3 Stream의 구성 요소
- 데이터 소스: 컬렉션, 배열, 파일 등.
- 중간 연산: 데이터를 변환하거나 필터링하는 작업 (lazy).
- 최종 연산: 결과를 생성하거나 Stream 파이프라인을 종료하는 작업.
1.4 Stream의 종류
- java.util.stream.Stream: 일반 객체 타입.
- IntStream, LongStream, DoubleStream: 기본형 타입의 Stream.
- Parallel Stream: 병렬로 처리되는 Stream.
2. 사용법
2.1 Stream 생성
Stream은 다양한 데이터 소스에서 생성할 수 있습니다.
2.1.1 컬렉션에서 생성
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();
2.1.2 배열에서 생성
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
2.1.3 Stream.builder() 사용
Stream<String> stream = Stream.<String>builder().add("a").add("b").add("c").build();
2.1.4 Stream.generate() 사용
Stream<Double> stream = Stream.generate(Math::random).limit(10);
2.1.5 Stream.of() 사용
Stream<String> stream = Stream.of("a", "b", "c");
2.2 중간 연산
중간 연산은 Stream을 변환하는 작업으로, 체이닝 가능하며 lazy하게 실행됩니다.
2.2.1 map
요소를 변환.
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.stream().map(n -> n * 2).forEach(System.out::println);
2.2.2 filter
조건에 맞는 요소만 선택.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().filter(name -> name.startsWith("A")).forEach(System.out::println);
2.2.3 sorted
정렬.
List<Integer> numbers = Arrays.asList(3, 1, 2);
numbers.stream().sorted().forEach(System.out::println);
2.2.4 distinct
중복 제거.
List<Integer> numbers = Arrays.asList(1, 2, 2, 3);
numbers.stream().distinct().forEach(System.out::println);
2.2.5 limit
처리할 요소 개수 제한.
Stream.generate(Math::random).limit(5).forEach(System.out::println);
2.2.6 skip
처음 N개의 요소 건너뜀.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().skip(2).forEach(System.out::println);
2.3 최종 연산
최종 연산은 Stream 파이프라인을 종료하고 결과를 생성합니다.
2.3.1 forEach
모든 요소에 작업 수행.
List<String> names = Arrays.asList("Alice", "Bob");
names.stream().forEach(System.out::println);
2.3.2 collect
결과를 컬렉션으로 변환.
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
2.3.3 reduce
요소를 하나로 축약.
int sum = Arrays.asList(1, 2, 3).stream().reduce(0, Integer::sum);
2.3.4 count
요소 개수 반환.
long count = Arrays.asList(1, 2, 3).stream().count();
2.3.5 anyMatch / allMatch / noneMatch
조건과 일치하는지 확인.
boolean anyMatch = Arrays.asList(1, 2, 3).stream().anyMatch(n -> n > 2);
3. 예시
3.1 학생 목록 처리
class Student {
String name;
int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
List<Student> students = Arrays.asList(
new Student("Alice", 85),
new Student("Bob", 75),
new Student("Charlie", 95)
);
// 점수가 80 이상인 학생의 이름 목록 출력
students.stream()
.filter(student -> student.getScore() >= 80)
.map(Student::getName)
.forEach(System.out::println);
4. Stream의 구성 요소
(1) 생성 (Creation)
Stream은 다양한 소스에서 생성됩니다:
- 컬렉션: List, Set, Map 등
- 배열: Arrays.stream(array)
- 파일/입출력: Files.lines(path)
- 범위/숫자: IntStream.range(), Stream.of()
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
(2) 중간 연산 (Intermediate Operations)
중간 연산은 여러 단계로 구성된 스트림 파이프라인을 생성합니다.
연산 설명 예시
filter() | 조건에 맞는 요소를 필터링 | stream.filter(x -> x.length() > 2) |
map() | 요소를 변환 | stream.map(String::toUpperCase) |
sorted() | 요소를 정렬 | stream.sorted() |
distinct() | 중복 제거 | stream.distinct() |
limit() | 개수를 제한 | stream.limit(5) |
skip() | 앞의 n개 요소를 건너뜀 | stream.skip(3) |
(3) 최종 연산 (Terminal Operations)
최종 연산은 스트림을 소비하고 결과를 반환합니다.
연산 설명 예시
collect() | 결과를 컬렉션으로 반환 | stream.collect(Collectors.toList()) |
forEach() | 각 요소에 대해 동작 수행 | stream.forEach(System.out::println) |
reduce() | 요소를 축소하여 하나의 값 반환 | stream.reduce(0, Integer::sum) |
count() | 요소 개수 반환 | stream.count() |
anyMatch() | 조건을 만족하는 요소가 있는지 확인 | stream.anyMatch(x -> x > 10) |
5. 장단점
5.1 장점
- 코드 간결성: 선언적 스타일로 데이터 처리 로직을 간단히 표현.
- 가독성 향상: 데이터 처리 흐름을 직관적으로 이해 가능.
- 병렬 처리 지원: parallelStream()으로 병렬 처리가 용이.
- 불변성 유지: 원본 데이터를 변경하지 않아 안전한 코드 작성.
5.2 단점
- 디버깅 어려움: 체이닝 방식으로 인해 중간 단계 디버깅이 복잡.
- 오버헤드: 작은 데이터셋에서는 성능 저하 가능.
- 병렬 처리 주의 필요: 병렬 처리 시 올바른 스레드 안전성 보장이 필요.
Java Stream API는 데이터 처리 작업을 간결하고 선언적으로 표현할 수 있는 강력한 도구입니다. 다양한 데이터 소스와의 통합, 지연 연산, 병렬 처리 지원 등 많은 이점을 제공하므로, 적절히 활용하면 생산성과 코드 품질을 크게 향상시킬 수 있습니다.
'Java' 카테고리의 다른 글
[Java/자바/면접] 얕은 복사와 깊은 복사에 대해서 (1) | 2025.01.31 |
---|---|
[Java] equals() 와 hashCode() 메서드의 관계 그리고 오버라이딩에 대해서 (0) | 2025.01.15 |
자바 객체지향 생활체조 9원칙 (0) | 2025.01.10 |
[Java] 자바 버전 (7, 8, 11, 17)에 대한 특징 정리 (0) | 2025.01.09 |
자바에서 1급 컬렉션에 대해 알아보기 (0) | 2025.01.06 |