Stream API 활용편5 (숫자형 스트림, 스트림 생산)
현재 공부하고 있는 도서의 Stream API 에 대한 자세한 설명이 나와 있어서, 같은 주제로 계속 블로깅을 하고 있습니다.
하지만 대략적인 부분이 끝이 난 것 같습니다. Stream API 소개는 이번 포스팅이 마지막입니다.
지난 Stream API 가 궁금하다면, 하단 글들을 참고해주세요. :-)
2016/08/26 - [개발이야기/함수형 프로그래밍] - Stream API 활용편1 (필터와 슬라이싱)
2016/08/27 - [개발이야기/함수형 프로그래밍] - Stream API 활용편2 (매핑)
2017/01/18 - [개발이야기/함수형 프로그래밍] - Stream API 활용편3 (검색과 매칭)
오늘 포스팅은 보통 전공 과목에서 흔히 말하는 기타 기능에 대해 알아보려고 합니다. 그러나 유용할 수 있죠? :-)
1. 숫자형 스트림으로 변환
람다를 포스팅할 때도 있었지만, 스트림 역시 원시타입의 스트림을 지원합니다. 보다 더 정확히 말하면, 숫자들에 대한 스트림을 지원하며 이 기본형 특화 스트림들은 sum, max, min 등 자주 사용하는 리듀싱 메소드를 제공해줍니다.
숫자 스트림을 사용하기 위해서는 map 의 파생 메소드인 mapToInt, mapToDouble, mapToLong 등의 메소드를 사용해야 합니다.
예제부터 한번 봐볼까요?
1 2 3 4 5 6 7 8 9 | List<Integer> numberList = Arrays.asList(1,2,3,4,5,6); System.out.println( numberList. stream(). mapToInt(Integer::intValue). sum()); // 출력 21 | cs |
간단한 Integer Collection 객체의 stream 에서 int value 의 stream 얻기 위해 mapToInt 메소드를 사용하였으며(언박싱), 리듀스 메소드 중 하나인 sum 을 사용하였습니다.
추가적으로 또다른 리듀싱 메소드인 max나 min 은 Optional 상태로 데이터가 제공됩니다. 컬렉션이 비어있는 상태에서 max 나 min 의 값이 무조건 존재한다는 것을 보장할 수 없기 때문이죠.
2. 숫자 범위 스트림
JAVA8 에서는 숫자형스트림에서 특정 범위안의 숫자 집합을 출력해주는 정적메소드인 range 와 rangeClosed 를 제공해줍니다. 두 메소드 모두 범위에 대한 인수를 받으며, 차이는 시작값과 종료값이 결과에 포함하는 여부입니다. (range 의 경우 결과에 포함되지 않습니다.)
사용예제는 다음과 같습니다. 아래 예제는 숫자 Stream 을 이용하여, 피타고라스의 수를 구하는 쿼리입니다.
1 2 3 4 5 6 7 | 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(v -> v[2]%1 == 0). forEach(v -> System.out.println(v[0] + " " + v[1] + " " + v[2])); | cs |
flatMap 을 통해, 두 개의 숫자 스트림을 엮었습니다. 첫번째 범위에서는 Stream 연산의 결과를 객체로 받기 위해 boxed 메소드를 사용하였습니다. boxed 객체를 사용하지 않으면, 오직 중간연산 결과로 숫자타입의 스트림밖에 생산할 수 없습니다. (왜냐하면 IntStream 은 숫자 스트림이기 때문이죠. ㅡㅡ^)
3. 스트림 만들기
컬렉션이나 배열은 다음과 같이 임의의 값으로 데이터를 만들 수 있습니다.
1 2 | String[] dataArray = new String[]{"남두현", "윤석진", "성지윤", "오진명"}; List<String> dataList = Arrays.asList(dataArray); | cs |
스트림 역시 임의의 값으로 스트림 만들기가 가능합니다. 꼭 컬렉션 클래스의 stream 키워드를 쓸 필요는 없는 것이죠. ㅡㅡ^
1 2 3 4 | String[] dataArray = new String[]{"남두현", "윤석진", "성지윤", "오진명"}; Stream<String> ofStream = Stream.of("남두현", "윤석진", "성지윤", "오진명"); Stream<String> arrayStream = Arrays.stream(dataArray); | cs |
4. 고정되지 않은 크기의 스트림 만들기
여태까지 Stream은 고정된 크기의 컬렉션을 통해서 만들곤 했습니다. 그러나 특정 Function, Supplier 타입의 메소드 레퍼런스 (람다 포함) 를 통해 무한한 Stream 을 만들 수 있습니다.
1 2 3 4 5 | // iterate 는 Function type 람다를 사용 Stream.iterate(0, n -> n + 2).forEach(System.out::println); // generate 는 Supplier type 메소드 레퍼런스 사용 Stream.generate(Math::random).forEach(System.out::println); | cs |
만들 수 있는 key-word 메소드는 iterate 와 generate 로 보통 limit 로 제한을 두어 사용하곤 합니다. 제한을 두지 않은 스트림을 언바운드 스트림(Unbounded Stream) 이라 하며, 특정 로직에서 무한한 결과 때문에 제대로된 결과를 얻을 수 없을 지도 모릅니다.
(그러나 위 예제 에서는 무한한 숫자가 출력이 될 것입니다. 컬렉션과 달리 Stream 은 즉시 생산을 하는 것이 특징이기 때문이죠. ㅡㅡ^)
이 것으로 Stream API 활용과 관련된 주제는 끝났습니다.
다음 포스팅 부터는 Stream 을 통한 데이터 수집과 관련된 주제로 진행을 하게 될 것 같습니다.
(스포일러를 하자면, 언제나 최종 연산을 구할 때 사용했던 Collector 와 관련된 이야기 입니다. ^^)
|
'개발이야기 > 함수형 프로그래밍' 카테고리의 다른 글
병렬처리(1) - Stream API 를 이용해 간단히 병렬화 하기. (0) | 2017.02.15 |
---|---|
Collectors! 데이터를 수집해보자. (0) | 2017.02.02 |
Stream API 활용편4 (리듀싱) (0) | 2017.01.23 |
Stream API 활용편3 (검색과 매칭) (0) | 2017.01.18 |
Stream API 활용편2 (매핑) (0) | 2016.08.27 |