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 을 종료시킨다는 점이 다르다고 생각된다.
'모던자바' 카테고리의 다른 글
모던 자바 인 액션 - 5장 마무리 (0) | 2022.12.07 |
---|---|
람다식, 메서드 참조를 이용하여 예외 발생시 성공할 때 까지 반복 구현 - 함수형 인터페이스 (0) | 2022.12.01 |
댓글