![[Java] Stream API](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRDlR%2FbtsqfzhYwXT%2FbRym86EkWO4OxabrL4IWf1%2Fimg.png)

목차
Stream ?
- Java 8 부터 등장
- 다양한 data source(배열, 콜렉션 자료구조)를 표준화된 방법을 다루기 위한 방법
요약. Java Stream은 데이터를 빠르고 편리하게 대량 처리할 수 있도록 해주는 래퍼 클래스이다.
특징
① 스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다. (Read Only)
List<Integer> source = List.of(6, 4, 2, 3, 1, 5);
List<Integer> sorted = source.stream().sorted().collect(Collectors.toList());
System.out.println(source); // [6, 4, 2, 3, 1, 5]
System.out.println(sorted); // [1, 2, 3, 4, 5, 6]
② 스트림은 Iterator 처럼 일회용이다.
- 재사용 불가하므로, 최종 연산이 완료된 경우 스트림을 재생성하여 사용해야 한다
- 최종 연산 처리 후 재호출할 경우 아래와 같은 에러 메시지가 출력된다
java.lang.IllegalStateException: stream has already been operated upon or closed
Stream<String> empty = Stream.empty();
assertThat(empty).isEmpty();
// Stream을 재생성하여 사용 (위에꺼 바로 사용하면 java.lang.IllegalStateException)
empty = Stream.empty();
assertThat(empty.count()).isEqualTo(0);
③ 최종 연산 전까지 중간 연산이 수행되지 않는다 (지연 연산)
④ 스트림은 작업을 내부 반복으로 처리한다
- 이로 인해 스트림은 디버깅이 어렵다 (단점)
⑤ 스트림의 작업을 병렬로 처리 가능 (병렬 스트림)
⑥ Stream<T> 외에 Primitive 타입의 스트림 API도 제공 (IntStream, LongStream, DoubleStream)
- Primitive 타입에 대한 유용한 메서드 제공함 - 예. sum(), max(), min(), count(), average()
- 오토박싱/언박싱의 비효율 제거됨
처리 순서
Step1. Stream 생성
- 배열, 콜렉션 자료 구조를 Stream 객체로 변환합니다.
- Collection 인터페이스 구현체는 stream() 호출
- 배열의 경우 Arrays.stream(..) 또는 Stream.of(..), 그리고 Primitive Type 의 Stream을 지원
Step2. 중간 연산
- 각 요소에 대해 연산을 수행한다
- 함수형 인터페이스 인자를 받아, 람다나 메서드 참조로 지정 가능
- filter(), map(), flatMap(), skip(), peek(), sorted(), limit()
Step3. 최종 연산
- 중간 연산을 통해 처리된 요소들을 모아 지정한 자료 형식으로 반환해줍니다.
- collect(), toArray(), sum(), count() 등
(참고) 개인적으로 Stream API를 직접 읽고, 해석하여 사용하기 위해서는 아래 6가지에 대해 공부할 필요성이 있다고 생각합니다
① Generic
② FunctionalInterface
③ Lambda Expression
⑤ Method Reference
⑥ Optional
절차형 vs 함수형 프로그래밍
1 ~ 10 까지의 수 중 짝수의 합을 구하는 간단한 예시를 생각해본다
1) 절차형 프로그래밍의 경우
int[] numbers = IntStream.rangeClosed(1, 10).toArray();
int total = 0;
for(int n : numbers) {
if(n % 2 == 0) total += n;
}
- 구체적인 구현 로직이 외부에 노출되는 외부 반복
- how 중심의 코드
- continue, break와 같은 keyword 사용 가능
- 코드의 조건이 추가될 경우 인덴트가 늘어나고 조건문이 복잡해짐
2) 함수형 프로그래밍의 경우
int total = Arrays.stream(numbers).filter(n -> n % 2 == 0).sum();
- 람다, 메서드 참조로 함수 인자를 사용하여 더욱 간결하게 표현가능
- 지역변수, keyword(continue, break 등)를 사용할 수 없다
- 구체적인 구현 로직이 내부 반복 수행되기 때문에 디버깅이 어렵다
- what 중심의 코드
- 코드의 조건이 추가되더라도 filter 메서드 체인을 통해 표현하여 간결하게 표현 가능
for와 stream 비교
1) int 배열 덧셈
2) int 배열 최대값 구하기
3) ArrayList 덧셈
4) ArrayList 최대값 구하기
성능 측정 예시
// 데이터 수 조절
List<Integer> data = IntStream.rangeClosed(1, 1000).boxed().collect(Collectors.toList());
long startTime = System.nanoTime();
// .. 측정 메서드
long endTime = System.nanoTime();
long result = (endTime - startTime);
// 측정 결과 ms
System.out.println(result * 1e-6);
결과적으로 Stream API가 상대적으로 For-Loop에 비해 느리다는 것을 확인 가능했다.
예전에 포스팅 중에 for-each와 stream 을 비교하는 글 읽은 적있는데, stream이 좋아도 상황에 따라 for-each를 사용하는 것이 더 낫다는 이유를 조금은 이해하게 될 수 있었다.
참고
https://medium.com/javarevisited/lets-go-to-the-java-stream-6db12f16eff5
'공부 > Java' 카테고리의 다른 글
[Java] Stream 중간 연산 (Stream Intermediate Operation) (0) | 2023.08.03 |
---|---|
[Java] Stream 생성 (파일 데이터 제외) (0) | 2023.08.03 |
[Java] 람다식을 더 짧게 - 메서드 참조 Method Reference (0) | 2023.08.02 |
[Java] Optional 클래스 메서드 (자바의 정석) (0) | 2023.08.01 |
[Java] Iterators 인터페이스 (자바의 정석) (0) | 2023.07.31 |

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!