Stream API(스트림 API)
Stream API가 나오게 된 배경
- 자바에서는 많은 양의 데이터를 저장하기 위하여 배열이나 컬랙션(Collection)을 사용하는데
- 이렇게 저장된 데이터에 접근하기 위하여 반복문이나 반복자(iterator)를 사용하여 매번 새로운 코드를 작성해야함
- 하지만 이렇게 작성된 코드는 길고 가독성이 떨어지며, 코드의 재사용이 거의 불가능함
- 즉, 정형화된 처리 패턴을 가지지 못하여 데이터 종류에 따라 다른 방법으로 데이터를 처리해야 했음
이러한 문제를 해결하기 위해 JDK8부터 Stream API을 도입함
- Stream API는 데이터를 추상화하여 다루므로, 다양한 방식으로 저장된 데이터를 조회하고 사용하기 위한 공통적인 방법을 제공함
- 따라서, Stream API를 이용하면, 배열이나 컬랙션뿐만 아니라 파일에 저장된 데이터도 모두 같은 방법으로 처리할 수 있음
[ JDK8 ]
- 자바는 객체 지향 언어이기 때문에, 기본적으로 함수형 프로그래밍이 불가능하였으나
- JDK8 버전부터 Stream, 람다 표현식, 함수형 인터페이스 등을 지원하면서, 자바에서 함수형으로 프로그램을 할 수 있는 API를 제공
개념
- 자바를 이용해 함수형으로 프로그래밍 할 수 있는 API를 제공해주는데
- 주로, 람다식을 활용하여 배열과 컬랙션 등의 데이터를 함수형으로 간단하게 처리할 수 있게 함
- 데이터의 종류와 상관 없이 같은 방식으로 데이터를 처리할 수 있도록 하는 함수들을 정의해 두었음
예제
1. 배열과 리스트 데이터를 정렬(sort)하여 출력
- sort() 메서드 사용 시, 원본 데이터 자체가 변경이 됨
public class Main {
public static void main(String[] args) {
// 배열
int [] numArr = {100, 0, 2, 5000, 30};
// List
List<Integer> numList = Arrays.asList(numArr);
// sort : 원본의 데이터가 직접 정렬됨
Arrays.sort(numArr);
Collections.sort(numList);
// 원본 데이터가 오름차순으로 변경되어 출력
for (int num: numArr) {
System.out.println(num); // 0,2,30,100,5000
}
for (Integer num : numList) {
System.out.println(num); // 0,2,30,100,5000
}
}
}
2. Stream을 사용하여, 배열과 리스트 데이터를 정렬(sort)하여 출력
- Stream을 사용하여
- 원본 데이터를 변경하지 않으면서도, 정렬의 기능을 사용할 수 있고
- 코드를 간략하게 작성할 수 있음
public class Main {
public static void main(String[] args) {
// 배열
Integer [] numArr = {100, 0, 2, 5000, 30};
// List
List<Integer> numList = Arrays.asList(numArr);
// 별도의 Stream을 생성(원본의 데이터와 별개)
Stream<Integer> listStream = numList.stream();
Stream<Integer> arrStream = Arrays.stream(numArr);
// 생성된 Stream의 데이터를 정렬하여 출력함
listStream.sorted().forEach(System.out::println); // 0,2,30,100,500
arrStream.sorted().forEach(System.out::println);
// 원본은 변경되지 않음
for (int num: numArr) {
System.out.println(num); // 100,0,2,5000,0
}
for (Integer num : numList) {
System.out.println(num); // 100,0,2,5000,0
}
}
}
Stream API의 특징
1. 스트림은 원본의 데이터를 변경하지 않음
2. 스트림은 단 1번만 사용할 수 있음 : 한번 사용하면 재사용이 불가
3. 내부 반복(internal iteration)을 통해 작업을 수행
4. 병렬 처리가 쉬움 : parallelStream() 메서드를 통하여 손쉬운 병렬처리를 지원함
5. 기본형 스트림을 제공
1. 원본의 데이터를 변경하지 않음
- 원본 데이터를 통해서, 별도의 Stream을 생성함
- 정렬, 필터링 등의 작업은 별도의 Stream 요소에서 처리함
2. Stream은 단 1번만 사용할 수 있음
- 한번 사용한 Stream은 재사용이 불가능
- Stream이 필요한 경우, 다시 생성해서 사용하여야 함
3. 내부 반복을 통하여 작업을 수행함
- 기존에는 반복 작업을 위해 for문이나 iterator문 등의 문법을 사용하였음
- 코드가 길어져서 가독성이 떨어짐
- Stream에서는 이러한 반복 문법이 내부에 숨겨져 있어, 간결한 코드 작성 가능
- forEach() 메서드에 반복문이 숨겨져 있어, 반복 작업을 내부에서 처리함
// 반복문이 forEach 함수에 숨겨져 있어, 내부적으로 반복 작업을 처리함
arrStream.sorted().forEach(System.out::println);
4. 기본형 스트림을 제공함
- String<Integer> 대신에 IntStream을 제공하여, 박싱과 언박싱의 불필요한 과정을 생략할 수 있고
- 숫자의 경우, 유용한 메서드를 제공함
- .sum(), .average()
Stream API의 동작 흐름 : 생성 - 중간 연산 - 최종 연산
Stream API는 3가지 단계를 걸쳐서 동작함
[ 3가지 단계 ]
1. Stream 생성하기
2. 중간 연산 : 가공하기
3. 최종 연산 : 결과 만들기
1. Stream 생성하기
- Stream 객체를 생성하는 단계
- 배열, 컬랙션, 파일 등 모든 종류의 데이터를 가지고 Stream을 생성할 수 있음
- Stream은 일회용이므로, 재사용이 불가능함
- 따라서, 필요하면 Stream을 다시 생성해서 사용해야 함
원본데이터타입 변수.stream() // Stream 생성하기
2. 중간 연산 : Stream 변환하기
- 원본의 데이터를 별도의 데이터로 변환(가공)하기 위한 중간 연산 단계
- 어떤 객체의 Stream을 원하는 형태로 만드는 단계이고
- 예를 들어, 배열을 오름차순으로 정렬하고 싶을 때
- 원본 데이터와 별도로 배열 객체의 Stream을 만들고 (생성하기)
- 이 Stream을 오름차순의 형태로 만드는 단계 (변환하기)
- 중간 연산을 한 리턴값은 Stream이므로, 필요한 만큼 중간 연산을 연결하여 사용할 수 있음
- 중간 연산의 결과가 Stream을 반환해야 하기 때문에, 생성된 Stream에 여러 개의 연산이 세미콜론(;) 없이 .으로 연결이 됨
- Stream 연산이 연결되어 있는 것을 스트림 파이프 라인(Stream PipeLine)이라고 함
원본데이터타입 변수.stream() // 생성하기
.sorted() // 가공하기
3. 최종 연산 : 결과 만들기
- 가공된 데이터로부터 원하는 결과를 만들기 위한 최종 연산
- 1번만 처리가 가능함
원본데이터타입 변수.stream() // 생성하기
.sorted() // 가공하기(오름차순하여)
.sum() // 결과만들기(총합을 구하기)
예제
public class StreamTest {
public static void main(String[] args) {
// 원본데이터: List
List<String> fruits = Arrays.asList(
"strawberry", "apple", "orange", "banana");
// Stream 연산
fruits
.stream() // Stream 생성
.map(String::toUpperCase) // 각 요소를 대문자로
.sorted() // 오름차순 정렬
.forEach(System.out::println); // 스트림 돌면서 출력
}
}
Stream과 Collection의 차이점
Stream API | Collection |
내부 반복을 통해 작업을 수행 (사용자가 반복문을 처리할 필요가 없음) |
외부 반복을 통해 작업 for-each을 통해 반복문을 만들어 연산을 처리 |
재사용이 불가 (단 1번만 사용할 수 있음) |
재사용이 가능 |
요청할 때만 요소를 계산하는 고정된 자료구조 (Stream에 요소를 추가하거나 삭제하는 것이 불가) |
Colletion에 포함되기 전에 각 요소들은 계산이 완료 되어 있어야 함 |
출처
'JAVA' 카테고리의 다른 글
[JAVA] Optional의 개념과 사용법, 주의할 점 (0) | 2024.05.01 |
---|---|
[JAVA] 스트림이 동작할 때(생성, 중간 연산, 최종 연산) 사용하는 메서드 (0) | 2024.05.01 |
[JAVA] 데몬 스레드 알아보기(특징, 사용 방법) (0) | 2024.04.30 |
[JAVA] 람다 표현식이란? (특징, 사용 문법) (0) | 2024.04.30 |
[JAVA] 자바 스레드 생성(Thread, Runnable) (0) | 2024.04.29 |