스트림의 소스가 될 수 있는 대상은 배열, 컬렉션, 임의의 수 등 다양하며 이 다양한 소스들로부터 스트림을 생성하는 방법에 대해 배워보자.
컬렉션의 최고 조상인 Collection에 stream()이 정의돼 있다. 따라서 자손인 List와 Set을 구현한 컬렉션 클래스들은 모두 이 메서드로 스트림을 생성할 수 있다.
stream()은 해당 컬렉션을 소스(source)로 하는 스트림을 반환한다.
Stream<E> stream() // Collection인터페이스의 메서드
List로부터 스트림은 아래와 같이 생성한다.
List<Integer> list = Arrays.asList(1,2,3,4,5); // 가변인자
Stream<Integer> intStream = list.stream(); // list를 소스로 하는 스트림 생성
forEach()는 지정된 작업을 스트림의 모든 요소에 대해 수행하게 된다.
intStream.forEach(System.out::println); // 스트림의 모든 요소를 출력한다
배열을 소스로 하는 스트림을 생성하는 메서드는 Stream과 Arrays에 static메서드로 정의돼 있다
Stream<T> Stream.of(T... values) // 가변인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)
예를 들어 문자열 스트림은 다음과 같이 생성한다.
Stream<String> strStream = Stream.of("a", "b", "c"); // 가변인자
Stream<String> strStream = Stream.of(new String[]{"a", "b", "c"});
Stream<String> strStream = Arrays.stream(new String[]{"a", "b", "c"});
Stream<String> strStream = Arrays.stream(new String[]{"a", "b", "c"}, 0, 3);
그리고 int, long, double과 같은 기본형 배열을 소스로 하는 스트림을 생성하는 메서드도 있다.
IntStream IntStream.of(int... values) // Stream이 아니라 IntStream
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array,int startInclusive, int endExclusive)
위 IntStream, LongStream, DoubleStream은 숫자관련 메서드, 오토박싱 문제로 따로 제공되는 기본형 Stream이다.
난수를 생성하는데 사용하는 Random클래스에는 아래와 같은 인스턴스 메서드들이 포함돼 있다. 각각의 타입의 난수를 반환한다.
IntStream ints()
LongStream longs()
DoubleStream doubles()
이 메서드들이 반환하는 스트림의 크기는 정해지지 않아'무한 스트림'이므로 limit()을 사용해 스트림의 크기를 제한해 주어야 한다. limit()은 스트림의 개수를 지정하는데 사용되며, 무한 스트림을 유한 스트림으로 만들어준다.
intStream intStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 5개의 요소만 출력한다.
아래의 메서드들은 매개변수로 스트림의 크기를 지정하기에 limit()없이 유한 스트림을 생성해 반환해준다.
IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles(long streamSize)
IntStream intStream = new Random().ints(5); // 크기가 5인 난수 스트림 반환
위 메서드들에 의해 생성된 스트림의 난수는 아래의 범위를 갖는다.
Integer.MIN_VALUE <= ints() <= Integer.MAX_VALUE
Long.MIN_VALUE <= longs() <= Long.MAX_VALUE
0.0 <= doubles() < 1.0
IntStream과 LongStream은 다음과 같이 지정된 범위의 연속된 정수를 스트림으로 생성해서 반환하는 range()와 rangeClosed()를 가지고 있다.
IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)
range()의 경우 경계의 끝인 end가 범위에 포함되지 않고, rangeClosed()의 경우에는 포함된다.
IntStream.range(1, 5) // 1, 2, 3, 4
IntStream.rangeClosed(1, 5) // 1, 2, 3, 4, 5
int보다 큰 범위의 스트림을 생성하려면 LongStream을 사용하면 된다.
Stream클래스의 iterate()와 generate()는 람다식을 매개변수로 받아서, 이 람다식에 의해 계산되는 값들을 요소로 하는 무한 스트림을 생성한다.
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) // 이전 요소에 종속적
static <T> Stream<T> generate(Supplier<T> s) // 이전 요소에 독립적
iterate()는 초기값(seed)로 지정된 값부터 시작해서, 람다식 f에 의해 계산된 결과를 다시 seed값으로 해서 계산을 반복한다. 아래의 evenStream은 0 부터 시작해서 값이 2씩 계속 증가하는 무한스트림이 된다.
Steram<Integer> evenStream = Steram.iterate(0, n -> n + 2); // 0, 2, 4, 6...
generate()도 iterate()처럼, 람다식에 의해 계산되는 값을 요소로 하는 무한 스트림을 생성해서 반환하지만, iterate와는 달리 이전 결과를 이용해 다음 결과를 계산하지는 않는다.
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(() -> 1);
한 가지 주의할 점은 iterate()와 generate()에 의해 생성된 스트림을 아래와 같이 기본형 스트림 타입의 참조변수로는 다룰 수 없다는 것이다.
IntStream evenStream = Stream.iterate(0, n -> n + 2); // 에러.
DoubleStream randomStream = Stream.generate(Math::random); // 에러.
굳이 필요하다면 mapToInt()와 같은 메서드를 사용해야한다.
IntStream evenStream = Steram.iterate(0, n -> n + 2).mapToInt(Integer::valueOf);
Stream<Integer> stream = evenStream.boxed(); // IntStream -> Stream<Integer>
java.nio.file.Files는 파일을 다루는데 유용한 메서드들을 제공한다. list()는 지정된 디렉토리에 있는 파일의 목록을 소스로 하는 스트림을 반환한다.
※ Path는 하나의 파일 또는 경로를 의미한다.
Stream<Path> Files.list(Path dir)
파일의 한 행(line)을 요소로 하는 스트림을 생성하는 메서드도 있다.
맨 아래의 lines()는 BufferedReader클래스에 속한 것인데, 파일 뿐만 아니라 다른 입력대상으로부터도 데이터를 행단위로 읽어올 수 있다.
Stream<String> Files.lines(Path path)
Stream<String> Files.lines(path path, Charset cs)
Stream<String> lines() // BufferedReader클래스의 메서드
요소가 하나도 없는 스트림을 생성할 수 있다. 스트림에 연산을 수행한 결과가 하나도 없을 때, null보다 빈 스트림을 반환하는 것이 낫다.
Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해 서반환한다.
long count = emptyStream.count(); // count의 값은 0