ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - Stream 사용시 알면 좋은 것들
    카테고리 없음 2021. 1. 3. 17:42

     

    스트림이 새로 나왔다고해서 무조건 좋은 것은 아니다. 개발 방법이라는 것이 그렇듯이 옛날에 나온 프레임워크, 언어라고 할지라도 최신 언어보다 더 좋은 점이 있고 안좋은 점이 있기 때문이다. 사실 좋고 안좋은 점이라기 보다는, 상황에 따라 성능 차이가 있을 뿐인 것 같다. 

     

    성능 - 더 빠르지는 않다.

    스트림을 사용하면 for문보다 훨씬 빠르겠지? 라는 생각을 할 수 있다. 왜냐면 최신 api이기 때문이다. 항상 그렇듯이 데이터의 개수가 작을 때는 문제가 없다. 그러나 데이터가 많아진다면 다른 이야기가 될 수 있다. 그래서 for 문, stream, parallel stream 3가지의 경우로 비교해보자. 약 10만개의 값이 숫자가 들어 있는 배열에서 가장 큰 값을 찾아내는 성능 테스트이다.

     

    결과는 다음과 같다.

    for-loop: 8ms
    Stream: 123ms
    Parallel Stream: 15ms

     

    생각했던 것보다 신기하게 for문이 1등을 했고 stream과는 15배라는 성능차이가 발생했다. for문을 사용하면 훨씬 더 빠른 이유는 원시적인 구문이기 때문에 매우 최적화 되어있다고 볼 수 있는 반면 스트림은 그렇지 않다. 또 for문은 index에 기반한 단순 메모리 접근이기에 오버헤드가 없기도하다. 그러나 stream을 사용하면 오버헤드가 생각보다 많이 생기게 돼서 이러한 결과나 나오게 됐다. 

     

     

    가독성

    Stream을 쓰면 가독성이 더 좋아질 것이라는 글들이 많이 있고 나도 그런 포스팅을 올렸다. 그러나 항상 그렇지는 않다는게 사실이다. 이중 포문을 사용해서 값을 프린트하는 예제를 보면 대충 감이 올 것이다. 이것은 착한 예이고 다른 복잡한 코드에서는 훨씬 더 길어지는 경우도 있다. 그러니 상황에 맞게 잘 사용해야겠다. 

     

    List<Integer> list = Arrays.asList(1, 2, 3);
     
    // Old school
    for (Integer i : list)
        for (int j = 0; j < i; j++)
            System.out.println(i / j);
     
    // "Modern"
    list.forEach(i -> {
        IntStream.range(0, i).forEach(j -> {
            System.out.println(i / j);
        });
    });
    

     

     

    변수 접근

    스트림을 이용하면서 람다 함수를 사용하게 되면 지역 변수에는 접근할 수 없게 된다. 생각보다 치명적인 문제이다.

     

    또 중간연산을 하게되면 이전의 값을 가지고 오고 싶을 때가 있을 수도 있는데 중간연산에서 map과 filter를 사용한다면 map과 flilter 둘 사이의 연산에는 값이 공유되지 않는다. 

     

     

    동작 방법

    Stream에 중간 연산이 여러개가 있다면 어떻게 작동하는지 알아보자. 예를 들어 아래와 같은 코드가 있고 리스트에는 값이 3개가 있다고 하자. 그러면 과연 어떻게 동작할까? 필자는 3개의 값이 모두 map을 돌고 filter로가고 collect가 되는지 알았다. 그러나 그렇지 않다.

    1개의 값이 map -> filter -> collect까지 가면 그 다음 값이 같은 순서로 작동한다. 그래서 가능하다면 filter, skip 같은 중간 연산을 먼저 사용하는 것이 성능에 좋다. 

     

    list.stream()
    	.map()
        .filter()
        .collect()

     

     

     

    댓글

Designed by Tistory.