배열에서 값을 꺼내기 위해 사용했던 for문..
하지만 이를 대체하기 위한 stream이 등장했었다.
그렇다면 지금 stream이 왜 쓰이고, for문보다 나은 점이 대체 무엇일까?
책 '자바의 정석'을 참고하며 나의 생각들을 정리하였다.
Collections.sort()
를, Array 정렬시 Arrays.sort()
를...)참고 공식 문서 : https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
위 코드는 어떤 코드인지 한눈에 읽힌다.
1. widgets이라는 stream object를 Collection.stream()
으로 만들고,
2. 각 원소마다, RED color만 추출한 후에,
3. RED color의 weight를 int로 변환하고
4. 모든 weight의 합을 구하여 sum에 저장한다.
이렇게 코드가 무엇을 하려는 지 읽기도 쉽고, 자칫 굉장히 길게 만들어질 수 있는 코드를 단축시킨다.
데이터 소스로부터 '읽기'만 할 뿐, 변경하지 않는다. 다만, 필요하다면 다음과 같이 저장할 순 있겠다.
List<String> sortedList = strStream.sorted().collect(Collectors.toList());
생성된 Stream으로 뭔 짓을 하면, 다시 사용할 수 없다.
Stream<String> strStream = strList.stream();
strStream.sorted().forEach(System.out::println);
int numOfStr = strStream.count(); //이것이 불가능하다.
스트림이 가져다 주는 간결성은 내부에서 반복적으로 처리해주는 작업 때문이다.
특히 Stream에는 forEach
가 굉장히 많이 사용된다.
이는 스트림 내에 구현되어 있고, 파라미터로 대입된 람다식을 stream의 모든 원소에 적용시킨다.
공식문서에 이런 설명이 있다.
The behavior of this operation is explicitly nondeterministic. For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism.
Stream은 병렬적으로 이루어진다는 내용이다. 즉, 각 원소마다 parallel하게 pipeline(연쇄 작업) 들이 이루어지고, 이에 따라 작업 순서를 보장하지 않는다.
연쇄작업을 거치는 동안, 연산 결과를 계속해서 stream으로 반환하고, 이를 이용해서 원하는 값을 도출할 수 있다.
반복적인 연산을 거치면서, 중간연산을 호출해도, 즉각적으로 연산이 이루어지지 않는다. 중간 연산은 최종연산이 수행되어야 비로소 수행된다.
요소의 타입이 T이면 보통 Stream<T>
를 사용하지만, 오토박싱과 언박싱으로 인한 비효율을 줄이기 위해 prmitive type을 다루는 Stream인 IntStream, LongStream, DoubleStream이 추가되었다.
Collection의 원소에 접근해야하는 횟수가 많아지게 되면, Array나 Collection이 좋다고 한다.
이는 코딩테스트에서도 확인할 수 있었다.
https://school.programmers.co.kr/learn/courses/30/lessons/136798
이 문제를 푸는데, 나는 다음과 같은 코드를 제출했었다.
import java.util.stream.*;
class Solution {
public int solution(int number, int limit, int power) {
int answer = 0;
int[] numbers = IntStream.rangeClosed(1,number).toArray();
for(int n:numbers){
int cnt = getCntDivisor(n) ;
if (cnt> limit){
answer += power;
}
else{
answer += cnt;
}
}
return answer;
}
public static int getCntDivisor(int n){
int cnt = 0;
for (int i = 1; i <= (int)Math.sqrt(n); i++){
if (n % i == 0) {
if (n/i ==i) cnt ++;
else cnt += 2;
}
}
return cnt;
}
}
대충 이런 시간이 걸렸다.
하지만 Stream이 아닌 Array를 사용한다면..?
전반적으로 빠른 속도로 끝내는 것을 볼 수 있다.
그러니 코딩테스트에 굳이 Stream을 사용해 시간적 효율을 줄이지 말자.
물론, 각 원소에서 해야할 작업이 많다면 Stream을 고려해볼만 하지만, 코딩테스트의 경우 그럴 경우는 거의 없다.
참고할만한 블로그 : https://brorica.tistory.com/entry/java-stream