Java

[모던 자바] 스트림(Stream) 만들기

Beekei 2022. 3. 4. 18:07
반응형

앞서 스트림이 무엇인지, 스트림을 어떻게 활용하는지 알아보았다.

이번엔 여러가지 스트림을 만드는 방법에 대해 알아보자.

 

값으로 스트림 만들기

임의의 수를 인수로 받는 정적 메서드 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를 사용한다.
스트림을 병렬로 처리하면서 올바른 결과를 얻으려면 상태가 없어야 하고, 만약 있다면 변경되어서는 안된다.
반응형