Java Stream_최종처리메소드

임준철·2021년 3월 14일
0

JavaAdvanced

목록 보기
10/15

최종 처리 메소드

동작메소드
루핑forEach()
매칭allMatch(), anyMatch(), noneMatch()
집계count(), max(), min(), average(), sum(), reduce()
조사findFirst(), findAny()
수집collect()

루핑

forEach() 메소드로 스트림 요소를 순차적으로 Consumer를 이용해 소비

  • forEach() - 요소들을 계속해서 반복해서 가져와라라는 의미이다.
    • stream.forEach(name -> System.out.println(name))으로 되어있을 때
      스트림에 가져온 요소를 name에 넣어서 하나씩 출력되게 하는 것이다.
    • 매개값의 형태는 Consumer 함수형 인터페이스 타입을 갖는다.
  • void forEach(Comsumer<? super T> action) : 스트림의 각 요소를 action으로 소비

매칭

Predicate 계열을 이용해 스트림 요소들이 특정 조건에 만족하는지 조사하는 메소드

  • boolean allMatch(Predicate<? super T> predicate) : 스트림의 모든 요소가 Predicate를 만족하면 true를 반환

  • boolean anyMatch(Predicate<? super T> predicate) : 스트림의 요소 중 하나라도 Predicate를 만족하면 true를 반환

  • boolean noneMatch(Predicate<? super T> predicate) : 스트림의 요소 중 하나라도 Predicate를 만족하지 않으면 true를 반환

     Stream<String> st0 = Stream.of("abc","cde","efg");
     System.out.println(st0.allMatch(s -> s.equals("abc"))); //false
     st0 = Stream.of("abc","cde","efg");
     System.out.println(st0.anyMatch(s -> s.equals("cde"))); //true
     st0 = Stream.of("abc","cde","efg");
     System.out.println(st0.noneMatch(s -> s.equals("abcde")));//true

조사

첫번째 요소 또는 아무 요소를 조사하는 최종 처리 메소드. 필터링 등으로 인해 스트림에 요소가 남아있는지 확인할 수 있다.

  • Optional<T> findFirst() : 스트림의 첫 요소 또는 empty Optional 객체를 반환
  • Optional<T> findAny() : 스트림의 아무 요소나 가지는 Optional 객체를 반환 (병렬 스트림일 때 사용한다.)
     int firstValue = Arrays.stream(new int[]{1,2,3,4,5})
                     .filter(n -> n%3 ==0)
                     .findFirst()
                     .getAsInt();

집계 (통계)

  • 최종 처리 기능
    • 카운팅, 합계, 평균값, 최대값, 최소값 등과 같이 하나의 값으로 산출한다.
    • 대량의 데이터를 가공해서 축소하는 리덕션이라고 볼 수 있다.
  • 기본 집계 메소드
    • 기본형 스트림의 통계 : count(), sum(), average(), min(), max()
    • T 타입 스트림의 통계 : count(), min(), max() (min, max의 경우 Comparator 필요)

reduce() 메소드 : 사용자 정의 집계 메소드

  • 모든 자료형에서 사용할 수 있는 집계 메소드이다!
  • 개발자가 프로그램화해서 다양한 집계(리덕션) 결과물을 만들 수 있다.
  • 특정한 값을 집계 할 수 있게 하기 위해서 reduce를 제공한다.

세가지 인자를 받아서 처리

  • accmulator : 각 요소를 처리하는 계산 로직이다. 각 요소가 올 때마다 중간 결과를 생성한다.
     Optional<T> reduce(BinaryOperator<T> accumulator) 
     //accumulator를 수행하고 Optional<T> 타입 반환
  • identity : 계산을 위한 초기값이다. stream이 비어서 계산할 값이 없더라도 이 값은 반한된다.
     T reduce(T identity, BinaryOperator<T> accumulator) 
     // identity를 초기값으로 하여, accumulator를 이용해 집계 연산
  • combiner : 병럴 stream에서 나눠 계산한 결과를 하나로 합쳐 반환한다.
     <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) 
     // combiner를 이용해 병렬 스트림 결합

메소드 형태 및 사용코드

인터페이스리턴 타입메소드
StreamOptionalreduce(BinaryOperator accumulator
Treduce(T identitiy, BianryOperator accmulator)
IntStreamOptionalIntreduce(IntBinaryOperator op)
intreduce(int indentity, IntBinaryOperator op)
LongStreamOptionalLongreduce(LongBinaryOperator op)
longreduce(long indentity, LongBinaryOperator op)
DoubleStreamOptionalDoublereduce(DoubleBinaryOperator op)
doublereduce(double indentitiy, DoubleBinaryOperator op)
  • reduce(BinaryOperator accumulator)

    • 이건 연산의 결과가 없으면 예외가 발생한다. NotSuchElementException 요소가 있어야 동작한다.
  • reduce(T identitiy, BianryOperator accmulator)

    • 연산의 결과가 없다면, 디폴트로 identity 사용 요소가 없어도 동작한다.
    • 연산의 결과가 없는 것은 요소가 없는 것이다. 그래서 연산의 결과로 디폴트 값으로 identity를 하는 것이다.
  • 매개변수

    • XXXBinaryOperator : 두 개의 매개 값을 받아 연산 후 리턴하는 함수적 인터페이스
    • identity : 스트림에 요소가 전혀 없을 경우 리턴될 디폴트 값
     System.out.println(IntStream.range(0,10).reduce(0,(value1,value2)->value1+value2)); //sum()
     // 1,2와 더한걸 3이랑 더하고 12 와 3을 더한걸 4 와 더함 .. 누적해서 더하는 것이다.
     // identity가 사용이 되는 이유는 처음 identitiy와 1을 더한값과 2를계산하기위해서
     // 0 [0,1,2,3,4,5,6,7,8,9] 0+0 =0 -> 0+1->1 1+2-> 3 과같은 연산 0~9까지 더한 연산
     // sum()으로도 나타낼 수 있다 sum,min,max등등 도 reduce로 구현되어 있다
    
     System.out.println(IntStream.range(0,10).reduce(Integer.MAX_VALUE,(value1,value2)->value1<value2? value1:value2));// min()

Optional 클래스

  • T 타입 객체의 null 여부에 따라 다르게 동작하는 Wrapper 클래스
  • 자바 8부터 추가된 값을 저장하는 값 기반 클래스
  • java.util 패키지의 Optional, OptionalDouble, OptionalInt, OptionLong 클래스를 말한다.
  • 집계 메소드의 리턴 타입으로 사용되어 집계 값을 가지고 있다. 이게 핵심이다!!
  • 저장된 값을 얻으려면 get(), getAsDouble(), getAsInt(), getAsLong()를 호출한다.
  • Optional 클래스의 정적 메소드를 이용해 Optional 객체 생성
    • public static <T> Optional<T> of(T value) : value가 null인 경우 NullPointerException을 발생시키는 Wrapping 메소드
    • public static <T> Optional<T> ofNullable(T value) : value가 null인 경우 empty()의 결과를 리턴하는 Wrapping 메소드
    • public static <T> Optional<T> empty() : 값이 비어있는 Optional 객체를 리턴
  • Optional 객체를 처리하는 메소드
    • public T get() : Optional의 값을 리턴하며, null일 경우 NullPointerException 발생
    • public void ifPresent(Consumer<? super T> consumer) : Optional 값이 null이 아닐 경우 consumer를 이용해 소비한다, 값이 저장되어 있지 않을 경우 디폴트 값(매개변수가 디폴트 값이 된다)
      • consumer로 집계 값을 받아서 처리하는 코드를 람다식으로 작성한다
    • public T orElse(T other) : Optional의 값이 null일 경우 other를 반환한다, 값이 저장되어 있을 경우 Consumer 에서 처리
    • public T orElseGet(Supplier<? extends T> other) : Optional의 값이 null일 경우 Supplier를 통해 공급받은 값을 반환한다.
    • public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X : Optional의 값이 null일 경우 exceptionSupplier에서 공급받은 예외를 throw
    double avg = list.stream()
            .mapToInt(Integer::intValue)
            .average()
            .getAsDouble();
    System.out.println(avg); //NotSuchElementException
   
    // isPresent()
    OptionalDouble optionalDouble = list.stream()
            .mapToInt(Integer::intValue)
            .average()

    if (optionalDouble.isPresent()){
        System.out.println("값이 있는 경우 : " + optionalDouble.getAsDouble());
    }else{
        System.out.println("방법 1-값이 없는 경우 : " + 0.0);
    
    // orElse()
    double avg = list.stream()
            .mapToInt(Integer::intValue)
            .average()
            .orElse(0.0;
    System.out.println("방법 2 : " + avg);
   
    // ifPresent()
    list.add(2);
    list.add(4);
    list.stream()
            .mapToInt(Integer::intValue)
            .average()
            .ifPresent(i-> System.out.println("방법 3 : "+ i));
    // 값이 없을 경우 false가 되서 종료가 된다. consumer로 했기 때문에 바로 값 출력

수집

필요한 요소를 수집하여 새로운 Collection으로 구성하여 반환하는 메소드

  • 최종 처리 기능으로 요소들을 수집 또는 그룹핑한다.
    • 필터링 또는 매핑된 요소들로 최종적으로 얻은요소들로 새로운 컬렉션 생성하는 것.
    • 요소들을 그룹핑하고, 집계를 할 수 있다.
  • Stream API는 JCF-> STREAM -> 처리 -> 결과
  • 결과가 출력일 수도 있고 값일 수도 있고, 다시 컬렉션으로 만들고 싶을 수 있다.
  • 요소가 여러개 있는 프레임워크 혹은 Arrays에서 스트림을 생성한 다음에 스트림에서 중간 처리를 쭉쭉한 다음에 마지막에 출력이나 값을 낼 수 있지만
    다음 중 3가지중 하나의 조건은 꼭 선택해야 한다.
    • 조건1. foreach()를 사용하거나
    • 조건2. 특정한 값을 리턴을 받거나(reduce())
    • 조건3. count,min,max 스트림을 간추려서 통계를 낼 수 있는 값들을 가져오던지 다시 컬렉션으로 만들어 주던지 해야한다.

보통 수집을 하기 위해서 필터링을 한다.

  • 스트림에서 필요한 요소만 필터링해서 별도의 컬렉션으로 만든다

  • collect라는 메소드 사용 collect(Collector <T,A,R> collector)

  • collect가 요소를 어떤 컬렉션에 수집할 것인지를 결정한다.

  • collect()메소드

    • <R,A> R collect(Collector<? super T,A,R) collector) : collector를 이용해 새로운 Collection R에 담아 반환
      • Collectors의 정적 메소드 : toList(), toSet(), toCollection(), toMap(), toConcurrentMap()
    • <R, A> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)
      : supplier를 통해 공급된 컨테이너 R에 accumulator를 이용해 T값을 저장. 병렬처리 스트림에 사용될 경우 combiner를 이용해 스레드별 컨테이너 R을 통합
  • Collector의 타입 파라미터

    • T : 요소
    • A : 누적기(accumulator) 요소를 컬렉션에 수집하는 역할을한다.
    • R : 요소가 저장될 새로운 컬렉션
      • T 요소를 A 누적기가 R에 저장한다.
리턴 타입메소드설명
Collector<T,?,Collection>Collectors.toCollection{Supplier}Supplier가 제공한 Collection에 저장
Collector<T,?,ConcurrentMap<K,U>>Collectors.toConcurrentMap(...)ConcurrentMap에 저장한다.
Collector<T,?,List>Collectors.toList()List에 저장
Collector<T,?,Map<K,U>>Collectors.toMap()Map에 저장
Collecotr<T,?,Set>Collectors.toSet()Set에 저장
  • Collectors.toConcurrentMap(...) - 멀티쓰레드 환경에서 쓰레드의 안전한 concurrentmap을 만들어서 거기에 요소를 수집하는 Collector를 얻는다.

  • A가 ? 인 이유

    • List, Set, Map 컬렉션에 누적할 경우 별도의 누적기가 필요 없다.
    • 컬렉터내부에서 이들 컬렉션에 저장하는 방법을 알고 있기 때문에 별도의 누적기가 필요 없음.
      String[] array = {"Java", "Is", "Fun", "Isn't", "It", "?"};
      List<String> list = Arrays.stream(array)
              .filter(s -> s.length() >= 3)
              .collect(Collectors.toList()); // ArrayList
              // .collect(Collectors.toCollection(LinkedList::new))
      System.out.println(list.getClass().getName() + ":" + list);
        
      Set<String> set = Arrays.stream(array)
              .filter(s -> s.length() >= 3)
              .collect(Collectors.toSet()); // HashSet
              // .collect(Collectors.toCollection(HashSet::new))
      System.out.println(set.getClass().getName() + ":" + set);
      
      Map<String, Integer> map = Arrays.stream(array)
              .filter(s -> s.length() >= 3)
              .collect(Collectors.toMap(s -> s, String::length)); // HashMap
              // .collect(Collectors.toCollection(s -> s, String::length, (oldVal, newVal) -> newVal, TreeMap::new))
      System.out.println(map.getClass().getName() + map);
  • Collectors의 정적 메소드를 이용한 그룹화와 분리

  • partitioningBy는 어떤 기준으로 무더기로 나누는 것 (2가지 true or false)

  • groupingBy는 어떤 기준으로 여러개 묶어 내는 것 (여러개)

    • public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
      : classifier를 key값으로, 해당하는 값의 목록을 List인 value로 가지는 Map으로 스트림을 수집하는 Collector를 반환

      • public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) : List 대신 downstream collector로 수집
      • groupingBy(R Function) - Map<R, List>입력받은게 function으로 출력됨 R타입에 따라 그룹이 됨 그래서 R이 키가 된다.
      • T를 String..length 로 해주면 R은 Integer
      • 결과 - 1: [...........], 2: [...........], 3: [...........]
      • 키를 기준으로 그룹화 하는 것이다 다 대 다 형태 n:m이면 m이 조금 작을 것이다
    • public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) : predicate 결과를 key로, 해당하는 값의 목록을 List value로 가지는 Map으로 스트림을 수집하는 Collector를 반환

      • public static <T, A, D> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream)) : List 대신 downstream collector로 수집
      • partitionBy(predciate)- Map<Boolean, List> 왜냐하면 이건 두가지로 나누는 것이기 때문에 predicate 계열을 가지고 참인지 거짓인지 판단을 해야 해서
      • 결과 - true :[......] , false: [......]
   String[] array = {"Java", "Is", "Fun", "Isn't", "It", "?"};
    
    Map<Character, List<String>> map1 = Arrays.stream(array)
            .collect(Collectors.groupingBy(s -> s.charAt(0)));
    System.out.println(map1);
    
    Map<Boolean, List<String>> map2 = Arrays.stream(array)
            .collect(Collectors.partitioningBy(s -> s.length() >= 3));
    System.out.println(map2);
  • 집계를 위한 Collector

    • Downstream collector로 집계를 위한 Collector를 사용할 경우 유용하다.
    • counting(), summingP(), averagingP(), maxBy(), minBy(), reducing()
    String[] array = {"Java", "Is", "Fun", "Isn't", "It", "?"};
      
      Map<Character, Long> map = Arrays.stream(array)
              .collect(Collectors.groupingBy(s -> s.charAt(0),
                                             Collectors.counting()));
      System.out.println(map);
profile
지금, 새로운 문을 열자! 문 저편에 무엇이 있을지 두렵더라도!!

0개의 댓글