Stream 정리
2023. 1. 27. 13:02ㆍ카테고리 없음
스트림(Stream)
- 다양한 데이터 소스(컬렉션, 배열 등)를 표준화된 방법으로 다루기 위한 것
- 그 전까지는 List, Set, Map 등의 사용 방법들이 서로 달랐다.
- 데이터 소스를 스트림으로 변환 후 여러 번의 중간연산과 마지막의 최종 연산을 통해 다를 수 있다.
- 순서
- 스트림 만들기
- 중간연산(반복 적용 가능, 연산 결과가 스트림)
- 최종연산 (스트림의 요소를 소모) -> 결과 리턴
list.stream() //스트림 만들기
.distinct() //중간연산
.limit(5) //중간연산
.sorted() //중간연산
.forEach(System.out::println) //최종연산
#스트림의 특징
- 데이터를 담고 있는 저장소 (컬렉션)이 아니다.
- 스트림은 원본 데이터 소스를 변경하지 않는다.(Read Only)
- 스트림은 lterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함)
- 최종 연산 전까지 중간연산을 수행되지 않는다.(lazy)
- 무제한일 수도 있다. (Short Circuit 메소드를 사용해서 제한할 수 있다.)
- 손쉽게 병렬 처리할 수 있다. (멀티 쓰레드 사용) (.parallel)
- 기본형 스트림으로 IntStream, LongStream, DoubleStream등 제공
- 오토박싱 등의 불필요한 과정이 생략됨.
- Stream<Integer> 대신에 IntStream을 사용하는게 더 효율적이다.
- 뿐만 아니라 숫자의 경우 더 유용한 메서드를 Stream<T>보다 더 많이 제공한다.(.sum(), .averge() 등)
스트림 사용하기
1. 스트림 생성
- Collection의 경우 .stream()으로 스트림 변환
Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5).stream();
- 객체 배열로부터 스트림 생성
Stream.of("a", "b", "c");
Stream.of(new String[]{"a", "b", "c"});
Arrays.stream(new String[]{"a", "b", "c"});
Arrays.stream(new String[]{"a", "b", "c"}, startIndex, EndIndex+1);
- 람다식 iterate(), generate()
//Stream.iterate(T seed, UnaryOperator f); 이전 결과에 종속적
Stream.iterate(0, n->n+2); //0, 2, 4, ... 무한 스트림
//generate는 초기값 지정x, 이전 결과와 무관하다.
Stream.generate(Math::random);
Stream.generate(()->1);
2. 스트림의 중간 연산
.distinct() //중복제거
.filter(Predicate<T> predicate) //조건에 안 맞는 요소는 제외
.limit(long maxSize) //maxSize 이후의 요소는 잘래냄
.skip(long n) //앞에서부터 n개 건너뛰기
.sorted() //기본 정렬로 정렬
.sorted(Comparator<T> comparator) //조건에 맞게 요소 정렬. 추가 정렬 기준을 제공할 때는 thenComparing()사용
//스트림의 요소를 변환. ex) map(File::getName), map(s->s.subString(3))
.map(Function<T> mapper)
//요소에 작업수행. 보통 중간 작업결과 확인으로 사용. peek(s->System.out.println(s))
.peek(Consumer<T> action)
//스트림의 스트림을 스트림으로 변환
//ex) Stream<String> strStrm=strArrStrm.flatMap(Arrays::stream)
.flatMap()
3. 스트림의 최종 연산
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) //모든 조건을 만족?
boolean anyMatch(Predicate<T> p) //조건을 하나라도 만족?
boolean noneMatch(Predicate<T> p) //모든 조건을 만족하지 않음?
Object[] toArray() //모든 요소를 배열로 반환
A[] toArray(IntFunction<A[]> generator) //특정 타입의 배열로 반환
//스트림의 요소를 하나씩 줄여가면서 계산
//아래에서 자세히 보자
Optional<T> reduce(BinaryOperator<T> accumulator)
//데이터를 변형 등의 처리를 하고 원하는 자료형으로 변환해 줍니다.
//아래에서 자세히 보자.
collect( ~ )
Stream API 사용 예시
Filter(Predicate)
- if문이랑 비슷
- 람다식의 리턴값은 boolean. true면 다음 단계 진행, false면 버려짐
classes.stream()
.filter(c->c.getTitle().startWith("spring"))
.forEach(c->System.out.println(oc.getId));
classes.stream()
.filter(Predicate.not(OnlineClass::isClosed))
.forEach(c->System.out.println(oc.getId));
- 예) 이름이 3글자 이상인 데이터만 새로운 스트림으로 변경하기
Map(Function) 또는 FlatMap(Function)
- stream을 우리가 원하는 모양의 새로운 스트림으로 변환
- 예) 각각의 File에서 String name만 새로운 스트림으로
- 예) string 타입 요소를 짤라서 새로운 스트림으로
map(File::getName)
map(s->s.subString(3))
limit(long) 또는 skip(long)
- 예) 최대 5개의 요소가 담긴 스트림을 리턴한다.
- 예) 앞에서 3개를 뺀 나머지 스트림을 리턴한다.
Stream.iterate(10, i->i+1)
.skip(10)
.limit(10)
.forEach(System.out::println)
anyMatch(), allMatch(), nonMatch()
- 예) k를 포함한 문자열이 있는지 확인한다. (true 또는 false를 리턴한다.)
boolean test=javaClasses.stream()
.anyMatch(oc->oc.getTitle().contains("k"));
- 예) 스트림에 있는 모든 값이 10보다 작은지 확인한다.
findFirst() VS findAny()
- 스트림을 직렬로 처리할 때는 차이가 없다.
- 하지만 병렬로 처리할 경우에 차이가 생기는데,
- findFirst()는 stream의 순서를 고려해, 가장 앞쪽에 있는 요소를 반환
- findAny()는 멀티 쓰레드에서 가장 먼저 찾은 요소를 반환. stream의 뒤쪽에 있는 요소가 반환될 수도 있다.
reduce 자세히 보기
- 스트림의 요소를 하나씩 줄여가며 누적연산 수행
- reduce(초기값, (누적 변수, 요소)-> 수행문)
Integer sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.reduce((total, y) -> total + y);
System.out.println("sum: " + s); //sum: 55
//초기값을 지정해 줄 수 있다.
Integer sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.reduce(10, (total, n) -> total + n);
System.out.println("sum: " + sum); //sum: 65
collect 자세히 보기
- Stream의 요소들을 우리가 원하는 자료형으로 변환할 수 있다.
stream.collect(Collectors.toSet()); //set으로 변환
stream.collect(Collectors.toList()); //list 변환
stream.collect(Collectors.joining()); //한개의 string으로 변환
stream.collect(Collectors.joining(", ")); //요소들 사이에 ","을 넣어서 한개의 string 반환