본문 바로가기
Language/Java

[Java 8] Stream - 3 (Numeric, Stream 생성)

by ocwokocw 2021. 2. 11.

- 출처: https://www.oracle.com/technical-resources/articles/java/ma14-java-se-8-streams.html

- Numeric Streams

앞에서 정수형의 합계를 계산하기 위해 reduce 메소드를 이용한 예제를 살펴보았다. 그러나 앞에서 살펴본 예제에는 숨겨진 비용이 있다. Integer 객체를 반복적으로 더할때 박싱이 적용되어 비용이 발생한다. 만약 아래코드처럼 sum 이라는 메소드를 호출할 수 있다면 코드가 더 깔끔하고 의도한 바가 분명해질것이다.

 

int statement = transactions.stream()
	.map(Transaction::getValue)
	.sum(); // error since Stream has no sum method

 

자바 8 에서는 3개의 기본형 Stream 인터페이스들(IntStream, DoubleStream, LongStream)을 제공한다. 각각 int, double, long 에 대해 특화된 stream 이라고 이해하면 된다.

stream을 특화된 stream 으로 변환하려면 mapToInt, mapToDouble, mapToLong 메소드를 이용하면 된다. 이 메소드들은 앞서 살펴본 map 메소드와 똑같이 동작하지만 Stream<T> 대신 특화된 Stream을 반환한다. 또한 boxed 연산자를 이용하면 기본형 stream을 객체 stream으로 역변환도 가능하다.

 

int statementSum = transactions.stream()
	.mapToInt(Transaction::getValue)
    .sum(); // works!

 

Numeric streams 의 또 다른 장점은 range 메소드를 제공한다는것이다. 예를 들어 1 부터 100까지 모든 숫자를 발생시키고 싶을때가 있다. 자바 8은 IntStream, DoubleStream, LongStream 에서 이용가능한 2 개의 정적 메소드(range, rangeClosed)를 제공한다.

두 메소드들 모두 첫번째 파라미터로 범위의 시작을 두번째 파라미터로 범위의 끝을 취한다. 그러나 range 는 시작과 끝을 포함시키지 않는 반면 rangeClosed 는 포함한다.아래 rangeClosed를 사용한 예제는 10이상 30이하의 모든 짝수 stream을 반환한다.

 

IntStream.rangeClosed(10, 30)
		.filter(n -> n % 2 == 0)
		.forEach(System.out::println);
10
12
14
16
.....
30

- Building Streams

Streams를 만드는데에는 여러개의 방법이 있다. 여태까지는 collection 에서 stream 을 얻고 숫자들에서 streams 를 얻어서 연산을 수행하였다. 값들로 부터 streams를 얻거나 배열, 파일로부터도 얻을 수 있다. 또 함수로 부터 stream 을 생산하여 무한 streams 를 만들수도 있다.

값들이나 배열로 부터 stream을 만드는일은 복잡하진 않다. 값들로 부터 얻을때는 Stream.of 메소드를 사용하거나 배열로 부터 얻을때는 Arrays.stream을 사용하면 된다.

 

Stream<Integer> numbersFromValues = Stream.of(1, 2, 3, 4);
int[] numbers = {1, 2, 3, 4};
IntStream numbersFromArray = Arrays.stream(numbers);

 

사실은 Stream.of 도 Arbitary Number of Arguments를 이용하여 인자를 받기 때문에 배열이나 동적개수의 인자를 받을뿐 내부적으로는 Arrays.stream을 이용하고 있다.

 

@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

 

또한 Files.lines 메소드를 이용하면 파일을 lines 의 Stream 으로 변환할 수 있다. 아래는 행의 개수를 세는 코드를 나타낸것이다.

 

long numberOfLines = 
    Files.lines(Paths.get(“yourFile.txt”), Charset.defaultCharset())
         .count();

 

- Infinite streams(무한 streams): Stream은 종결 연산자를 만날때 즉 요청이 있을 때 연산을 수행한다고 하였다. Stream.iterate와 Stream.generate 2개의 메소드가 있다. 이 메소드들은 함수에서 Stream을 생산하는 메소드이다.

 

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {

 

함수의 내용이 좀 복잡하지만 어쨌든 반환형은 Stream 이다. 그러나 원소들은 요청시에만 계산되기 때문에 이 2 연산들은 원소를 "무한대"로 생산할 수 있다. 고정된 collection 에서 stream을 만들 때처럼 stream은 고정된 크기를 가지지 않는다. 이것이 우리가 infinite stream 이라고 칭하는 개념이다.

아래 예제는 iterate를 이용하여 10의 배수인 모든 수를 만드는 Stream을 제작한것이다. iterate 메소드는 초기값으로 0을 그리고 2번째 인자로 lambda(UnaryOperator<T>)를 이용하여 새로운 값을 만들고 있다.

 

Stream<Integer> numbers = Stream.iterate(0, n -> n + 10);

 

limit 연산자를 이용하면 infinite stream을 고정된 크기의 stream으로 변환할 수 있다. 예를 들어 stream의 크기를 5로 제한할 수 있다.

 

numbers.limit(5).forEach(System.out::println); // 0, 10, 20, 30, 40

'Language > Java' 카테고리의 다른 글

[Java 8] Optional  (0) 2021.02.11
[Java 8] Stream - 4 (flatMap, collect)  (0) 2021.02.11
[Java 8] Stream - 2 (기본 연산)  (0) 2021.02.11
[Java 8] Stream - 1 (Stream 개요)  (0) 2021.02.11
[Java 8] 람다(Lambda) - 3  (0) 2021.02.11

댓글