본문 바로가기
Modern java in action

[modern java in action] stream(4)

by 상국이 2021. 6. 24.
728x90

4-5 리듀싱

스트림의 모든 요소를 반복적으로 처리해서 값을 도출하는 연산을 리듀싱 연산이라고 한다. 함수형 프로그래밍 언어 용어로는 이 과정이 마치 종이를 작은 조각이 될 때까지 반복해서 접는 것과 비슷하다는 의미로 폴드라고 부른다.

  1)요소의 합 (reduce)

int sum = numbers.stream().reduce(0, (a,b) -> a + b);        //누적값 계산

 

reduce( 초기값 , 두 요소를 조합해서 새로운 값을 만드는 BinaryOperator<T>) 

  2)초기값 없음

Optional<Integer> sum = numbers.stream().reduce((a , b) -> (a + b);)

 

이런경우 Optional객체를 반환한다. 스트림에 아무 요소도 없는 경우 초기값이 없으므로 reduce는 합계를 반환할 수 없다. 따라서 합계없음을 가리키기 위해 Optional객체로 감싼 결과를 반환한다.

  3) 최댓값 / 최솟값

최댓값, 최솟값을 찾을 때도 reduce를 활용할 수 있다.

Optional<Integer> max = numbers.stream().reduce(Integer::max);

 

Integer::max 대신 Integer::min을 넘겨주면 최소값을 구할 수 있다. 

 * reduce 메서드의 장점과 병렬화

 → reduce를 이용하면 내부 반복이 추상화되며 내부 구현에서 병렬로 reduce를 실행할 수 있게 된다.

 * 스트림 연산 : 상태 없음과 상태 있음

 → map, filter등은 입력 스트림에서 각 요소를 받아 0 또는 결과를 출력 스트림으로 보낸다. 따라서 내부 상태를 갖지 않는 연산이다. reduce, sum, max 같은 연산은 연산 결과를 누적할 내부 상태가 필요하다.

 → 반면 sorted나 distinct같은 연산은 과거의 이력을 알고 있어야 한다. 따라서 데이터 스트림의 크기가 크거나 무한이라면 문제가 생길 수 있다. 이러한 연산을 내부 상태를 갖는 연산이라 한다.

4-6 숫자형 스트림

  1) 기본형 특화 스트림

    (1) 숫자 스트림으로 매핑

    → 스트림을 특화 스트림으로 변활할 때는 mapToInt, mapToDouble, mapToLong 세 가지 메서드를 가장 많이 사용한다.

int calories = menu.stream()                        //Stream<Dish>반환
                   .mapToInt(Dish::getCalories)     //IntStream 반환
                   .sum();                          //스트림이 비어있으면 기본값 0반환

   

    (2) 객체 스트림으로 복원하기

    → boxed메서드를 이용해서 특화 스트림을 일반 스트림으로 변환할 수 있다.

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);    //스트림 -> 숫자스트림
Stream<Integer> stream = intStream.boxed();                           //숫자스트림 -> 스트림

   

    (3) 기본값 OptionalInt

    → 스트림에 요소가 없는 경우와 실제 최댓값이 0인 경우를 가리기 위해

OptionalInt maxCalories = menu.stream()
                              .mapToInt(Dish::getCalories)
                              .max();
//값이 없는 경우 기본 최대값을 명시적으로 설정
int max = maxCalories.orElse(1);

 

  2) 숫자 범위

  → 특정 범위의 숫자를 이용해야 하는 경우

IntStream evenNumbers = IntStream.rangeClosed(1,100)         //1~100의 범위
        .filter(n -> n % 2 == 0);                             //그중 짝수

 

  3) 숫자 스트림 활용

피타고라스 수

stream.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)

 

집합 생성

stream.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
      .map(b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)});

 

b값 생성

IntStream.rangeClosed(1,100)
        .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
        .mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)});

 

a값 생성

Stream<int[]> pythagoreanTriples = IntStream.rangeClosed(1,100).boxed()
                                           .flatMap(a -> IntStream.rangeClosed(a, 100)
                                                                  .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
                                                                  .mapToObj(b -> new int[]{a,b, (int)Math.sqrt(a * a + b * b)}));

 

 * 개선점 : 제곱근을 두 번 계산함 → 형식을 만족하는 세 수를 만든 다음에 원하는 조건에 맞는 결과만 필터링

Stream<double[]> pythagoreanTriples = IntStream.rangeClosed(1,100).boxed()
                                           .flatMap(a -> IntStream.rangeClosed(a, 100)
                                                                  .mapToObj(b -> new int[]{a,b, (int)Math.sqrt(a * a + b * b)})
                                                                  .filter(t -> t[2] % 1 == 0));

 

4-7 스트림 만들기

  1) 값으로 스트림 만들기

Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);

 

  2) null이 될 수 있는 객체로 스트림 만들기

Stream<String> values =
Stream.of("config", "home", "user")
      .flatMap(key -> Stream.ofNullable(System.getProperty(key)));

 

  3) 배열로 스트림 만들기

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();

 

  4) 파일로 스트림 만들기

long uniqueWords = 0;
try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                       .distinct()
                       .count();
}catch(IOException e){
    ;
}

 

  4) 함수로 무한 스트림 만들기

 → 고정된 컬렉션에서 고정된 크기로 스트림을 만들었던 것과는 달리 고정되지 않은 스트림

    (1) iterate메서드

    → iterate는 요청할 때마다 값을 생성할 수 있으며 끝이 없으므로 무한 스트림을 만든다. 이러한 스트림을 언바운드 스트림이라고 표현현다.

IntStream.iterate(0, n -> n + 4)
         .takeWhile(n -> n <100)
         .forEach(System.out.println);

   

    (2) generate메서드

    → generate메서드는 Supplier<T> 를 인수로 받아서 새로운 값을 생성한다.  / 생산된 각 값을 연속적으로 계산하지 않는다.

IntStream twos = IntStream.generate(new IntSupplier(){
    public int getAsInt(){
        return 2;
    }
});
728x90

댓글