본문 바로가기
모던자바

스트림의 활용 및 생성 - 모던 자바 인 액션

by 임동무 2022. 12. 1.

1. Stream 을 이용하여 피타고라스 수 만들기

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

결론부터 말하면 위의 코드를 이용하여 피타고라스 수를 만들 수 있다.

하나하나 뜯어보면

1. 먼저 IntStream 의 rangeClosed(a,b) 메서드는 inclusive 한 범위내의 숫자를 stream 으로 반환해주는 메서드이다. 이를 통해 1~100 까지의 숫자로 이루어진 stream 을 생성할 수 있다.

 

2. flatMap 은 stream 들을 평면화하기 위한 기능이다.

피타고라스 수를 만들기 위해서는 a 와 b 두 수가 필요하기 때문에 두 번의 map 과정을 하나의 스트림으로 평면화 해준다.

 

3. ( a -> IntStream.rrangeClosed(a,100) ) 은 첫 번째 숫자 a 에 대해서 이에 해당하는 a 보다 큰 수 b를 생성한다. 이 때, 우리는 b의 값이 무엇인지 모르기 때문에 IntStream 을 통해서 b 를 범위내의 숫자를 가진 IntStream 형태로 만든다.

 

4. mapToObj() 는 IntStream 의 각 요소를 다른 객체로 변환해주는 과정이다.

 

5. 여기서 가장 윗줄의 boxed 를 사용하는 이유는 Stream<T> 의 map 메서드를 사용하기 위해서이다. boxed() 를 사용하지 않는다면 rangeClosed() 는 IntStream 을 반환하며 flatMap 또한 IntStream 를 반환형으로 갖는다.

즉, 뒤에 연쇄되어 있는 flatMap 또한 IntStream 의 형태를 가져야 하므로 flatMap 내부의 동작이 IntStream 을 반환해야한다는 이야기이다.

하지만 boxed() 를 이용하면 IntStream 을 Stream<T> 형태로 변환할 수 있으며 Stream<T> 의 flatMap 메서드를 사용할 수 있기 때문에 map 메서드를 이용해서 Stream<U> 의 반환형을 가질 수 있기 때문에 boxed() 를 추가해준다.

 

6. IntStream 의 요소 a 하나에 대해서 범위내의 모든 b의 각 요소들을 각 double 배열에 넣어주며 이때, Math.sqrt(a*a+b*b) 를 이용하여 c 값 또한 double 타입으로 넣어준다.

 

7. 마지막 filter 를 통해 해당 Stream<double[]> 의 각 요소 중, 피타고라스 수의 값이 정수를 갖는 배열들을 추출해준다.

(t[2] % 1 == 0) 의 조건을 이용하면 정수만 추출할 수 있다.

 

 

2. 스트림 생성

Stream 을 생성하는 방식은

Stream.of() 를 이용하여 생성하는 방식,

Arrays.stream( array ) 를 이용하여 배열을 스트림으로 만드는 방식 등 여러가지 방식이 있다.

 

이중에서도 무한 스트림을 생성하는 방식이 있다. iterate 와 generate 를 이용한다.

기본적으로 무한 스트림을 생성하기 때문에 limit() 와 연결하여 종료지점을 명시해준다.

이 중에서 iterate 를 이용하여 위에서 했던 피타고라스 수를 추출해보자.

 

기본적인 사용법은 아래와 같다.

        Stream.iterate(10, n -> n + 2)
                .limit(10)
                .forEach(System.out::println);

iterate() 는 첫 번째 파라미터로 초기값을 시작으로 두번 째 파라미터를 이용한 연산으로 도출되는 모든 값들을 계속해서 stream 의 요소로 포함시킨다. 

 

이를 피타고라스 수에 적용해보면

        List<double[]> collect = Stream.iterate(1, n -> n + 1)
                .flatMap(n -> IntStream.rangeClosed(n, 100)
                        .mapToObj(m -> new double[]{n, m, Math.sqrt(n * n + m * m)})
                        .filter(doubles -> doubles[2] % 1 == 0))
                .limit(10)
                .collect(Collectors.toList());

이전과 동일한 방법으로 IntStream 의 rangeClosed 를 이용하여 수를 생성한 후 피타고라스 수가 정수가 되는 값들만 filter() 를 이용하여 추출한다.

다만 이전과 다른점은 생성을 Stream<Integer> 방식으로 하기 때문에 boxing 관련 오류가 발생하지 않는다는 점

(기본적으로 int 값을 Integer 로 박싱하는 과정은 동일하게 적용되기 때문에 성능상의 차이는 없을 거라고 생각됨)

그리고 a 의 마지막 범위를 지정하는 것이 아니라, 추출할 개수를 지정해서 stream 을 종료시킨다는 점이 다르다고 생각된다.

댓글