ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Stream(스트림) - 1. 스트림이란
    Java 2020. 2. 13. 14:50
    반응형

    Stream이란?

     데이터 소스를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의

    데이터 소스를 추상화했다는 것

     데이터 소스가 무엇이던 간에 같은 방식으로 다룰 수 있게 되었다는 것

     코드의 재사용성이 높아진다는 것

    Stream을 이용하면?

     배열이나 컬렉션뿐만 아니라 파일에 저장된 데이터도 모두 같은 방식으로 다룰 수 있음

    String[] strArr = { "aaa", "ddd", "ccc" };
    List<String> strList = Arrays.asList(strArr);
    
    // Stream 생성
    Stream<String> strStream1 = strList.stream();
    Stream<String> strStream2 = Arrays.stream(strArr);
    
    // 데이터를 읽은 후 정렬해서 화면에 출력 - 데이터 소스를 정렬하는 것은 아님
    strStream1.sorted().forEach(System.out::println);
    strStream2.sorted().forEach(System.out::println);
    
    // Stream이 아니었다면?
    Arrays.sort(strArr);
    Collections.sort(strList);
    for(String str : strArr) {
        System.out.println(str);
    }
    for(String str : strList) {
        System.out.println(str);
    }
    
    /* 참고 아래 두개는 같은 것임 */
    System.out::println
    (str) -> System.out.println(str)

    Stream은 데이터 소스를 변경하지 않음

     Stream은 데이터 소스로부터 데이터를 읽기만 할 뿐, 데이터 소스를 변경하지 않는다는 차이가 있음

     필요시 정렬한 결과를 컬렉션이나 배열에 담아서 반환할 수도 있음

    // 정렬된 결과를 새로운 List에 담아서 반환
    List<String> sortedList = strStream2.sorted().collect(Collectors.toList());

    스트림은 일회용이다.

     스트림은 Iterator처럼 일회용임

     Iterator로 컬렉션의 요소를 모두 읽고 나면 다시 사용할 수 없는 것처럼, 스트림도 한번 사용하면 닫혀서 다시 사용할 수 없음

     필요시 다시 생성해야 함

    strStream1.sorted().forEach(System.out::println);
    int numOfStr = strStream1.count(); // 에러, 스트림이 이미 닫힘

    스트림은 작업을 내부 반복으로 처리

     스트림을 이용한 작업이 간결할 수 있는 비결 중 하나는 바로 '내부 반복'

    내부 반복 : 반복문을 메서드의 내부에 숨길수 있다는 것을 의미

     forEach()는 스트림에 정의된 메서드 중의 하나로 매개변수에 대입된 람다식을 데이터 소스의 모든 요소에 적용

    즉, forEach()는 메서드 안으로 for문을 넣은 것. 수행할 작업은 매개변수로 받음

    for(String str : strList) {
        System.out.println(str);
    }
    
    stream.forEach(System.out::println);
    
    /* forEach */
    void forEach(Consumer<? super T> action {
        Objects.requireNonNull(action); // 매개변수의 Null Check
        
        for (T t : src) {
            action.accept(T);
        }
    }

    스트림의 연산

     스트림이 제공하는 다양한 연산을 이용해서 복잡한 작업들을 간단히 처리할 수 있음 (like DB에 SELECT 문으로 질의(query)하는 것과 같은 느낌)

    중간연산과 최종연산

     스트림이 제공하는 연산은 중간 연산과 최종 연산으로 분류할 수 있음

     중간연산 : 연산결과가 스트림인 연산. 스트림에 연속해서 중간연산할 수 있음

     최종연산 : 연산결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한 번만 가능

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

     모든 중간 연산의 결과는 스트림이지만, 연산 전의 스트림과 같은 것은 아님

     위의 예와 달리 모든 스트림 연산을 나누어쓰면 다음과 같음.

    String[] strArr = { "dd", "aaa", "CC", "cc", "b" }
    Stream<String> stream = Stream.of(strArr); // 문자열 배열이 소스인 스트림
    Stream<String> filteredStream = stream.filter(); // 걸러내기(중간 연산)
    Stream<String> distinctedStream = stream.distinct(); // 중복제거(중간 연산)
    Stream<String> sortedStream = stream.sort(); // 정렬(중간 연산)
    Stream<String> limitedStream = stream.limit(5); // 스트림 자르기(중간 연산)
    int total total = Stream.count(); // 요소 개수 세기 (최종 연산)

    스트림 중간 연산 - map(), flatMap()이 핵심

    // 중복 제거
    Stream<T> distinct()
    
    // 조건에 안 맞는 요소 제외
    Stream<T> filter(Predicate<T> predicate)
    
    // 스트림의 일부를 잘라냄
    Stream<T> limit(long maxSize)
    
    // 스트림의 일부를 건너뜀
    Stream<T> skip(long n)
    
    // 스트림의 요소에 작업수행
    Stream<T> peek(Consumer<T> action)
    
    // 스트림의 요소를 정렬
    Stream<T> sorted()
    Stream<T> sorted(Comparator<T> comparator)
    
    // 스트림의 요소를 변환
    Stream<R>    map(Function<T, R> mapper)
    DoubleStream mapToDouble(ToDoubleFunction<T> mapper)
    IntStream    mapToInt(ToIntFunction<T> mapper)
    LongStream   mapTOLong(TOLongFunction<T> mapper)
    
    Stream<R>    flatMap(Function<T, Stream<R>> mapper)
    DoubleStream flatMapToDouble(Function<T, DoubleStream> m)
    IntStream    flatMapToInt(Function<T, IntStream> m)
    LongStream   flatMapToLong(Function<T, LongStream> m)

    최종 연산 - reduce()와 colelct()가 핵심

    Optional은 일종의 Wrapper Class로 내부에 하나의 객체를 저장할 수 있음
    // 각 요소에 지정된 작업 수행
    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<> p) // 아무것도
    
    // 스트림의 모든 요소를 배열로 반환
    Object[] toArray()
    A[]      toArray(IntFunction<A[]> generator)
    
    // 스트림의 요소를 하나씩 줄여가면서(리듀싱) 계산
    Optional<T> reduce(BinaryOperator<T> accumulator)
    T reduce (T identity, BinaryOperator<T> accumulator)
    U reduce (U identity, BiFunction<U,T,U> accumulator, BinaryOperator<T> combiner)
    
    // 스트림의 요소를 수집
    // 주로 요소를 그룹화하거나 분할한 결괄르 컬렉션에 담아 반환하는 데 사용
    R collect(Collector<T,A,R> collector)
    R Collect(Supplier<R> supplier, BiConsumer<R,T> accumulator, BiConsumer<R, R> combiner) 
    

    지연된 연산

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

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

    중간 연산을 호출하는 것은 단지 어떤 작업이 수행되어야하는지를 지정해줄 뿐

    Stream<Integer>와 IntStream

     요소의 타입이 T인 스트림은 기본적으로 Stream<T>

     오토박싱&언박싱으로 인한 비효율을 줄이기위해 데이터 소스의 요소를 기본형으로 다루는 스트림이 제공됨 - IntStream, LongStream, DoubleStream

     일반적으로 Stream<Integer>대신 IntStream을 사용하는 것이 더 효율적임

     IntStream에는 int타입의 값으로 작업하는데 유용한 메서드들이 포함되어 있음

    병렬 스트림

     스트림으로 데이터를 다룰 때의 장점 중 하나 - 병렬 처리가 쉬움

     병렬 스트림은 내부적으로 fork&join프레임웍을 이용해서 자동으로 연산을 병렬으로 수행함

    사용자는 스트림에 parallel()이라는 메서드를 호출해서 병렬로 연산을 수행하도록 지시하면 됨

    단, 병렬처리가 항상 더 빠른 결과를 가져오진 않음

    기본 - 병렬이 아님

     sequential() 메서드를 호출하면 병렬이 아니게 처리됨

     하지만 기본이 병렬이 아니므로, parallel()을 호출한 것을 취소할 때만 사용됨

    parallel() & sequential()

     새로운 스트림을 생성하진 않고, 스트림의 속성을 변경할 뿐

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

    'Java' 카테고리의 다른 글

    Stream(스트림) - 3. 스트림 중간 연산  (0) 2020.03.09
    Stream(스트림) - 2. 스트림 생성하기  (0) 2020.02.13
    Lambda Expression 2. Function 패키지  (0) 2020.02.08
    Lambda Expression 1. 람다식  (0) 2020.02.08
    Enum(열거형)  (0) 2020.02.08

    댓글

Designed by Tistory.