/////
Search
Duplicate
8️⃣

스트림 만들기

스트림이 데이터 처리 질의를 표현하는 강력한 도구임을 충분히 확인했을 것이다.
stream 메서드로 컬렉션에서 스트림을 얻을 수 있었다. 그뿐만 아니라 범위의 숫자에서 스트림을 만드는 방법도 설명했다.
이밖에도 다양한 방식으로 스트림을 만들 수 있다. 이 절에서는 일련의 값, 배열, 파일, 심지어 함수를 이용한 무한 스트림 만들기 등 다양한 방식으로 스트림을 만들어본다.

1. 값으로 스트림 만들기

예를 들어 다음 코드는 Stream.of로 문자열 스트림을 만드는 예제다.
스트림의 모든 에제를 대문자로 변환하 후 문자열을 하나 씩 출력한다.
Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action"); stream.map(String::toUpperCase).forEach(System.out::println);
Java
복사
다음처럼 empty 메서드를 이용해서 스트림을 비울 수 있다.
Stream<String> emptyStream = Stream.empty();
Java
복사

2. null이 될 수 있는 객체로 스트림 만들기

자바 9에서는 nullable한 객체를 스트림으로 만들 수 있는 새로운 메소드가 추가되었다. 때로는 null이 될 수 있는 객체를 스트림으로 만들어야할 수 있다.
예를 들어 System.getProperty는 제공된 키에 대응하는 속성이 없으면 null을 반환한다.
이런 메소드를 스트림에 활용하려면 다음처럼 null을 명시적으로 확인해야 했다.
String homeValue = System.getProperty("home"); Stream<String> homeVlaueStream = homeValue == null ? Stream.empty() : Stream.of(value);
Java
복사
Stream.ofNullable을 통해 다음처럼 코드를 구현할 수 있다.
Stream<String> homeValueStream = Stream.ofNullalbe(System.getProperty("home"));
Java
복사
null이 될 수 있는 객체를 포함하는 스트림값을 flatMap과 함께 사용하는 상황에서는 이 패턴을 더 유용하게 사용할 수 있다.
Stream<String> values = Stream.of("config", "home", "user") .flatMap(key -> Stream.ofNullable(System.getProperty(key)));
Java
복사

3. 배열로 스트림 만들기

배열을 인수로 받는 정적 메서드 Arrays.stream을 이용해서 스트림을 만들 수 있다.
예를 들어 다음처럼 기본형 int로 이루어진 배열을 IntStream으로 변환할 수 있다.
int[] numbers = {2, 3, 5, 7, 11, 13}; int sum = Arrrays.stream(numbers).sum();
Java
복사

4. 파일로 스트림 만들기

파일을 처리하는 등의 I/O 연산에 사용하는 자바의 NIO API(비블록 I/O)도 스트림 API를 활용할 수 있도록 업데이트되었다.
java.nio.file.Files의 많은 정적 메서드가 스트림을 반환한다.
예를 들어 Files.lines는 주어진 파일의 행 스트림을 문자열로 반환한다.
지금까지 배운 스트림 연산을 활용하면 다음 코드처럼 파일에서 고유한 단어 수를 찾는 프로그램을 만들 수 있다.
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) { // 파일을 열다가 예외가 발생하면 처리한다. }
Java
복사
Files.lines로 파일의 각 행 요소를 반환하는 스트림을 얻을 수 있다.
스트림의 소스가 I/O 자원이므로 이 메소드를 try/catch 블록으로 감쌌고 메모리 누수를 막으려면 자원을 닫아야 한다.
기존에는 finally 블록에서 자원을 닫았다.
Stream 인터페이스는 AutoCloseable 인터페이스를 구현한다. 따라서 try 블록 내의 파일 자원은 자동으로 관리된다.
line에 split 메서드를 호출해서 각 행의 단어를 분리할 수 있다.
각 행의 단어를 여러 스트림으로 만드는 것이 아니라. flatMap으로 스트림을 하나로 평면화했다.
마지막으로 distinctcount를 연결해서 스트림의 고유 단어 개수를 계산한다.

5. 함수로 무한 스트림 만들기

스트림 API는 함수에서 스트림을 만들 수 있는 두 정적 메서드 Stream.iterateStream.generate를 제공한다.
두 연산을 이용해서 무한 스트림, 즉 고정된 컬렉션에서 고정된 크기로 스트림을 만들었던 것과는 달리 크기가 고정되지 않은 스트림을 만들 수 있다.
iterategenerate에서 만든 스트림은 요청할 때마다 주어진 함수를 이용해서 값을 만든다. 따라서 무제한으로 값을 계산할 수 있다.
하지만 보통 무한한 값을 출력하지 않도록 limit(n) 함수를 연결해서 사용한다.
iterate 메서드
Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println);
Java
복사
iterate 메서드는 초깃값과 람다를 인수로 받아서 새로운 값을 끊임없이 생산할 수 있다.
예제에서는 람다(n → n + 2) 즉 이전 결과에 2를 더한 값을 반환한다. 결과적으로 iterate 메서드는 짝수 스트림을 생성한다.
자바 9의 iterate 메소드는 프레디케이트를 지원한다.
예를 들어 0에서 시작해서 100보다 크면 숫자 생성을 중단하는 코드를 다음과 같이 작성할 수 있다.
IntStream.iterate(0, n -> n < 100, n -> n + 4) .forEach(System.out::println);
Java
복사
iterate 메소드는 두 번째 인수로 프레디케이트를 받아 언제까지 작업을 수행할 것인지의 기준으로 사용한다.
generate 메서드
iterate와 비슷하게 generate도 요구할 때 값을 계산하는 무한 스트림을 만들 수 있다.
iterate와 달리 generate는 생산된 각 값을 연속적으로 계산하지 않는다.
generateSupplier<T>를 인수로 받아서 새로운 값을 생산한다. 다음 예제를 살펴보자.
Stream.generate(Math::random) .limit(5) .forEach(System.out::println);
Java
복사
이 코드는 0에서 1 사이의 임의의 double 자료형 숫자 5개를 만든다.
이번에도 명시적으로 limit을 사용해 스트림의 크기를 한정했다.
만약 limit이 없었다면 스트림은 언바운드 상태가 된다.