앞서 스트림이 무엇인지, 스트림을 어떻게 활용하는지 알아보았다.
이번엔 여러가지 스트림을 만드는 방법에 대해 알아보자.
값으로 스트림 만들기
임의의 수를 인수로 받는 정적 메서드 Stream.of를 이용해서 스트림을 만들 수 있다.
Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
stream.map(String::toUpperCase).forEach(System.out::println);
다음 처럼 empty 메서드를 이용해서 스트림을 비울 수 있다.
Stream<String> emptyStream = Stream.empty();
null이 될 수 있는 객체로 스트림 만들기
java9에서는 null이 될 수 있는 개체를 스트림으로 만들 수 있는 새로운 메소드가 추가되었다.
때때로 null이 될 수 있는 객체를 스트림으로 만들어야 할 때가 있다.
ofNullable 메소드를 사용하면 값이 null 일때는 빈 스트림을 반환하고, null이 아닐때는 생성된 스트림으로 반환한다.
String value = null;
Stream<String> nullableStream = Stream.ofNullable(value);
// Stream<String> nullableStream = value == null ? Stream.empty() : Stream.of(value);
배열로 스트림 만들기
배열을 인수로 받는 정적 메서드 Arrays.stream을 이용해서 스트림을 만들 수 있다.
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
파일로 스트림 만들기
파일을 처리하는 등의 I/O 연산에 사용되는 자바의 NOI API(비브록 I/O)도 스트림 API를 활용할 수 있도록 업데이트 되었다. java.nio.file.Files의 많은 정적 메서드가 스트림을 반환한다.
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) {
...
}
위 코드는 Files.lines라는 정적 메서드로 파일을 읽어 스트림으로 반환하고 있다.
반환된 문자열 스트림에 띄어쓰기(" ")로 배열화하고 flatMap메서드를 사용해 스트림으로 평면화 시킨 후 중복을 없앤 갯수를 반환하여 uniqueWords 변수에 저장했다. 이처럼 File을 스트림을 변환해 연산이 가능하다.
함수로 무한 스트림 만들기
스트림 API는 함수에서 스트림을 만들 수 있는 두 정적 메서드 Stream.iterate와 Stream.generate를 제공한다.
두 연산을 이용해서 무한 스트림(크기가 고정되지 않은)을 만들 수 있다.
iterate와 generate에서 만든 스트림은 요청할 때마다 주어진 함수를 이용해서 값을 만든다. 따라서 무제한은 값을 계산할 수 있다. 하지만 보통 무한한 값을 출력하지 않도록 limit(n) 함수를 함께 연결해서 사용한다.
iterate
iterate는 초깃값과 람다를 인수로 받아서 새로운 값을 끊임없이 생산할 수 있고, 초깃값은 스트림 첫번째 요소로 반환된다.
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
기본적으로 iterate는 기존 결과에 의존해서 순차적으로 연산을 수행하고, 요청할 때마다 값을 생산할 수 있으며 끝이 없으므로 무한 스트림을 만든다. 이러한 스트림을 언바운드 스트림(unbounded stream)이라고 표현한다.
generate
iterate와 비슷하게 generate도 요구할 때 값을 계싼하는 무한 스트림을 만들 수 있다.
하지만 iterate와 달리 생성된 각 값을 연속적으로 계산하지 않는다.
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
일반적으로 연속된 일련의 값을 만들 때는 iterate를 사용하고, 병렬 처리처럼 상태가 없어야 하는 상황에는 generate를 사용한다.
스트림을 병렬로 처리하면서 올바른 결과를 얻으려면 상태가 없어야 하고, 만약 있다면 변경되어서는 안된다.
'Java' 카테고리의 다른 글
[모던 자바] Collector 인터페이스 소개 및 구현 예제 (0) | 2022.03.08 |
---|---|
[모던 자바] 컬렉터(Collector)란 무엇인가? (0) | 2022.03.07 |
[모던 자바] 스트림(Stream) 활용하기 (0) | 2022.03.03 |
[모던 자바] 스트림(Stream)이란 무엇인가? (0) | 2022.03.02 |
[모던 자바] 람다 표현식을 조합할 수 있는 유용한 메서드 (0) | 2022.03.02 |