•
스트림이 데이터 처리 질의를 표현하는 강력한 도구임을 충분히 확인했을 것이다.
•
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으로 스트림을 하나로 평면화했다.
◦
마지막으로 distinct와 count를 연결해서 스트림의 고유 단어 개수를 계산한다.
5. 함수로 무한 스트림 만들기
•
스트림 API는 함수에서 스트림을 만들 수 있는 두 정적 메서드 Stream.iterate와 Stream.generate를 제공한다.
•
두 연산을 이용해서 무한 스트림, 즉 고정된 컬렉션에서 고정된 크기로 스트림을 만들었던 것과는 달리 크기가 고정되지 않은 스트림을 만들 수 있다.
•
iterate와 generate에서 만든 스트림은 요청할 때마다 주어진 함수를 이용해서 값을 만든다. 따라서 무제한으로 값을 계산할 수 있다.
•
하지만 보통 무한한 값을 출력하지 않도록 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는 생산된 각 값을 연속적으로 계산하지 않는다.
•
generate는 Supplier<T>를 인수로 받아서 새로운 값을 생산한다. 다음 예제를 살펴보자.
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
Java
복사
◦
이 코드는 0에서 1 사이의 임의의 double 자료형 숫자 5개를 만든다.
◦
이번에도 명시적으로 limit을 사용해 스트림의 크기를 한정했다.
◦
만약 limit이 없었다면 스트림은 언바운드 상태가 된다.