우주먼지
Published 2023. 3. 1. 17:10
Stream Languages/Java

💡 Stream

많은 수의 데이터를 다룰 때 컬렉션이나 배열에 데이터를 담고 원하는 결고를 얻기 위해 for문이나 iterator를 사용해서 코드를 작성해왔다.

그러나 이런 방식으로 작성된 코드는 너무 길고 가독성과 재사용성이 떨어진다.

또 다른 문제는 데이터 소스마다 다른 방식으로 다뤄야 한다는 단점이 있다.

예를 들면 List를 정렬할 때는 Collections.sort()를 사용하고,
배열을 정렬할 때는 Arrays.sort()를 사용해야 한다.

이러한 문제를 해결하기 위해 만든것이 Stream이다.

스트림은 데이터 소스를 추상화하고 데이터를 다루는데 자주 사용되는 메서드들을 정의 해놓았다.

 

스트림의 특징

  • 데이터 소스(원본)을 변경하지 않는다.
  • Iterator처럼 일회용이다.
  • 작업을 내부 반복으로 처리한다.

예를 들어, 문자열 배열과 같은 내용의 문자열을 저장하는 List가 있을 때

String[] strArr = { "aaa","ccc","bbb" };
List<String> strList = Arrays.asList(sttArr);

이 두 데이터 소스르 기반으로 스트림 생성

Stream<String> stream1 = Arrays.stream(strArr);
Stream<String> stream2 = strList.stream();

두 스트림으로 데이터 소스의 데이터를 읽어서 정렬하고 화면에 출력, 소스 원본이 정렬되는 것은 아님

    Stream<String> stream1 = Arrays.stream(strArr)
            .sorted()
            .forEach(System.out::println);

    Stream<String> stream2 = strList.stream()
            .sorted()
            .forEach(System.out::println);

💡 스트림의 연산

스트림이 제공하는 다양한 연산을 이용해 복잡한 작업을 단순화 할 수 있다.

예시

stream.distinct().limit(5).sorted().forEach(System.out::println)

중간연산

연산결과를 스트림으로 반환하기 때문에 중간연산을 연속해서 연결할 수 있다.

  • distinct()
  • limit()
  • sorted()

최종연산

스트림의 요소를 소모하면서 연산을 수행하므로 단 한번만 연산한다.

  • forEach()

 

중간 연산 목록

중복 제거

  • Stream distinct()

조건에 안맞는 요소 제외

  • Stream filter(Predicate predicate)

요소의 일부를 잘라냄

  • Stream limit(long maxSize)

일부를 건너뜀

  • Stream skip(long n)

요소에 작업 수행

  • Stream peek(Consumer action)

요소 정렬

  • Stream sorted() & sorted(Comparator comparator)

요소 변환

  • Stream map(Function<T,R> mapper)
  • DoubleStream mapToDouble()
  • IntStream mapToInt()
  • LongStream mapToLong()
  • Stream flatMap(Function<T,R> mapper)
  • DoubleStream flatMapToDouble()
  • IntStream flatMapToInt()
  • LongStream flatMapToLong()

 

최종 연산 목록

각 요소에 지정된 작업 수행

  • void forEach(Consumer<? super T> action)
  • void forEachOrdered<Consumer<? super T> action)

요소의 개수 반환

  • long count()

최대값 & 최소값 반환

  • Optional max(Comparator<? super T> comparator)
  • Optional min(Comparator<? super T> comparator)

요소 하나를 반환

  • Optional findAny() - 아무거나 하나
  • Optional findFirst() - 첫번째 요소

주어진 조건을 모든 요소가 만족시키는지 아닌지 확인

  • boolean allMatch(Predicate p) - 모두 만족 하는지
  • boolean anyMatch(Predicate p) - 하나라도 만족 하는지
  • boolean noneMatch(Predicate p) - 모두 만족하지 않는지

모든 요소를 배열로 반환

  • Object[] toArray()
  • A[] toArray(IntFunction<A[]> generator)

요소를 하나씩 줄여가면서(Reducing) 계산

  • Optional reduce(BinaryOperator accumulator)
  • T reduce(T identity, BinaryOperator accumulator)
  • U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator combiner)

요소를 수집, 주로 요소의 그룹화하거나 분할한 결과를 컬렉션에 담아 반환하는데 사용

  • R collect(Collector<T,A,R> collector)
  • R collect(Supplier supplier, BiConsumer<R,T> accumulator, BiConsumer<R,R> combiner)

 

지연된 연산

스트림의 연산에서 한가지 중요한 점은 최종 연산이 수행되기 전까지는 중간연산이 수행되지 않는다.

스트림에 대해 distinct()나 sort()같은 중간 연산을 호출해도 즉각적인 연산이 수행되는 것이 아니다.

최종 연산이 수행되어야 비로소 스트림의 요소들이 중간 연산을 거쳐 최종 연산에서 소모된다.

 

Stream와 IntStream

요소의 타입이 T인 스트림은 기본적으로 Stream이지만, 오토박싱 & 언박싱으로 인한 비효율을 줄이기 위해 데이터 소스의 요소를 기본형으로 다루는 스트림, IntStream, LongStream, DoubleStream이 제공된다.

일반적으로 Stream 대신 IntStream을 사용하는 것이 더 효율적이고,
int 타입의 값으로 작업하는데 유용한 메서드들이 포함되어 있다.


💡 병렬 스트림

스트림으로 데이터를 다룰때 장점 중 하나가 병렬 처리가 쉽다는 것이다.

내부적으로 fork & join 프레임워크를 이용해서 자동적으로 연산을 병렬로 수행한다.

우리가 할일은 그저 스트림에 parallel()이라는 메서드를 호출해 병렬로 연산을 수행하도록 지시하는 것이다.

반대로 병렬로 처리되지 않게 하려면 sequential()을 호출하면 된다.

모든 스트림은 기본적으로 병렬 스트림이 아니므로 sequential()을 호출할 필요가 없다.

이 메서드는 parallel()을 호출한 것을 취소할 때만 사용한다.

// strStream()을 병렬 스트림으로 전환
int sum = strStream.parallel()
    .mapToInt(s -> s.length())
    .sum();

병렬처리가 항상 더 빠른 결과를 얻게 해주는 것은 아니다.

'Languages > Java' 카테고리의 다른 글

Multi Catch Block  (0) 2023.03.25
Stream 생성  (0) 2023.03.17
Single & Multi Thread  (0) 2023.03.01
Thread  (0) 2023.03.01
J2EE  (0) 2023.02.20
profile

우주먼지

@o귤o

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

검색 태그