[Stream API] 스트림이란?
스트림(Stream)
1. 스트림이란?
과거 Collection이나 Iterator와 같은 인터페이스를 이용해서 컬렉션을 다루는 방식을 표준화했지만, 각 Collection Class에는 같은 기능의 메서드를 중복해서 정의되어있음.
ex) List 정렬 시 Collections.sort()를 사용해야하고, Array 정렬 시 Arrays.sort()를 사용해야함
이를 해결하고 만든 것이 '스트림(Stream)'!!
스트림은 데이터 소스를 추상화하고, 데이터를 다루는데 사용되는 메서드를 정의해 놓음.
* 스트림의 장점
1) 데이터 소스가 무엇이던 같은 방식으로 다룰 수 있게됨
2) 코드의 재사용성이 높아짐
*스트림을 사용하지 않는 코드
public class StreamStudy {
public static void main(String[] args) {
/**
* Array 와 List 정의
*/
String[] strArr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrays.asList(strArr);
/**
* 과거 코드
*/
Arrays.sort(strArr);
Collections.sort(strList);
for (String str : strArr) {
System.out.println(str);
}
for (String str : strList) {
System.out.println(str);
}
}
}
* 스트림을 이용한 코드
public class StreamStudy {
public static void main(String[] args) {
/**
* Array 와 List 정의
*/
String[] strArr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrays.asList(strArr);
/**
* 스트림을 이용한 코드
*/
Stream<String> strStream1 = strList.stream();
Stream<String> strStream2 = Arrays.stream(strArr);
/**
* 스트림 데이터 졍렬 및 출력
*/
strStream1.sorted().forEach(System.out::println);
strStream2.sorted().forEach(System.out::println);
}
}
1.1 스트림은 휘발성!
스트림을 한번 사용하면 이후 재사용 불가!!
strStream1.sorted().forEach(System.out::println);
int numOfStr = strStream1.count(); // 에러발생. 이미 스트림이 닫힘!!
1.2 스트림은 작업을 내부 반복으로 처리
void forEach(Consumer<? super T> action) {
Object.requireNonNull(action); // 매개변수 Null 확인
for(T t : src) { // 내부 반복
action.accept(t);
}
}
1.3 스트림 연산
중간 연산 : 연산 결과가 스트림인 연산. 스트림에 연속해서 중간 연산 가능!!
최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 한번만 가능!!
// dintinct() : 중간연산
// limit(5) : 중간연산
// sorted() : 중간연산
// forEach() : 최종연산
stream.distinct().limit(5).sorted().forEach(System.out::println)
* 하나씩 뜯어보기!!
public static void main(String[] args) {
String[] strArr = {"dd", "aaa", "CC", "cc", "bb"};
Stream<String> stream = Stream.of(strArr); // 문자열 배열이 소스인 스트림
Stream<String> upperStrStream = stream.map(word -> word.toUpperCase()); // 대문자로 변환(중간 연산)
Stream<String> filteredStream = upperStrStream.filter(str -> str.contains("CC")); // 필터링(중간 연산)
Stream<String> distinctedStream = filteredStream.distinct(); //중복제거(중간 연산)
Stream<String> sortedStream = distinctedStream.sorted(); // 정렬(중간 연산)
sortedStream.forEach(System.out::println); // 출력(최종 연산)
}
* 중간연산과 최종연산 메서드 정리
※ 주로 사용하는 메서드 : map(), flatMap(), reduce(), collect()
중간연산 | 설명 |
Stream<T> distinct() | 중복 제거 |
Stream<T> filter(Predicate<T> predicate) | 조건에 안 맞는 요소 제외 |
Stream<T> limit(long maxSize) | maxSize 만큼만 가져옴 |
Stream<T> skip(long n) | n 만큼 건너뜀 |
Stream<T> sorted() Stream<T> sorted(Comparator<T> comparator) |
정렬 |
* map() Stream<R> map( Function<T,R> mapper ) DoubleStream mapToDouble( toDoubleFunction<T> mapper ) IntStream mapToInt( toIntFunction<T> mapper ) LongStream mapToLong( ToLongFunction<T> mapper ) * flatMap() Stream<R> flatMap( Function<T, Stream<R>> mapper ) DoubleStream flatMapToDouble( Function<T, DoubleStream> mapper ) IntStream flatMapToInt( Function<T, IntStream> mapper) LongStream flatMapToLong( Function<T, LongStream> mapper) |
스트림 요소 반환 |
최종 연산 | 설명 |
void forEach(Consumer<? super T> action) void forEachOrdered(Consumer<? super T> action) |
각 요소에 지정된 작업 수행 |
long count() | 스트림 요소 개수 반환 |
Optional<T> max(Comparator<? super T> comparator) Optional<T> min(Comparator<? super T> comparator) |
스트림의 최대값/최소값을 반환 |
Optional<T> findAny() // 아무거나 하나 반환 Optional<T> findFirst() // 첫 번째 요소 반환 |
요소 하나 반환 |
boolean allMatch(Predicate<T> p) // 모두 만족 시 true boolean anyMatch(Predicate<T> p) // 하나라도 만족 시 true boolean nonMatch(Predicate<T> p) //모두 불만족 시 true |
주어진 조건의 만족 여부 확인 |
Object[] toArray() A[] toArray(IntFunction<A[] generator> |
모든 요소를 배열(Array)로 반환 |
* reduce() Optional<T> reduce(BinaryOperator<T> accumulator) T reduce(T identity, BinaryOperator<T> accumulator) U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner) |
스트림 요소를 하나씩 줄여가며(Reducing) 계산 |
* collect() R collect(Collector<T,A,R> collector) R collect(Supplier<R> supplier, BiConsumer<R,T> accumulator, BiConsumer<R,R> combiner) |
스트림 요소를 수집. 요소를 그룹화 or 분할한 결과를 컬렉션에 담아 반환 |
1.4 지연된 연산
스트림 연산은 최종 연산이 수행되기 전까지 중간 연산이 수행되지 않음.
1.5 Stream<Integer> vs IntStream
스트림에서 오토박싱 & 언박싱으로 인한 비효율을 줄이기 위해 기본형 스트림(IntStream, LongStream, DoubleStream)을 제공
일반적으로, Stream<Integer> 보다 IntStream 사용이 더 효율적!!
1.6 병렬 스트림
스트림의 장점 중 하나는 병렬처리가 쉽다는 것인데, 병렬 스트림은 내부적으로 for&join프레임워크를 이용해 자동적으로 병렬연사을 수행
사용은 스트림에 parallel() 메서드를 호출하면 되며, 반대로 병렬처리하지 않게 하려면 sequential()을 호출하면 됨.
(단, 모든 스트림은 default로 병렬 스트림이 아니므로 sequential()을 호출할 필요 없음)
int sum = strStream.parallel() // 병렬 스트림으로 전환
.mapToInt(s -> s.length())
.sum();