ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 자바 스트림(Stream) API 내부 동작 알아보기
    프로그래밍/JAVA 2022. 10. 14. 10:59

     

    자바 스트림 작업은 모두 LinkedList 구조를 통해 내부적으로 저장되고 내부 저장 구조에서 각 단계에는 다음 구조를 따르는 비트맵이 할당된다. (Stream Flags)

    Stream internal flags

    • SIZED : 스트림의 크기를 아는가?
    • DISTINCT : 스트림의 요소가 전부 중복이 없는가?
    • SORTED : 요소가 원래 순서대로 정렬되었는가?
    • ORDERED : 요소가 스트림화 되는 순서가 컬렉션에서도 유지되는가?


    위와 같은 비트맵 정보를 통해 자바는 스트림 최적화를 실행하기 때문에 이는 매우 중요하다. 각각의 작업은 다른 비트맵을 지우고, 설정하고, 유지하게 된다.

    예를들어, 스트림의 map 메서드같은 경우 데이터가 변경될 수 있으므로 DISTINCT와 SORTED 비트맵을 지운다. 그러나 스트림의 크기는 수정되지 않으므로 SIZED 비트맵은 유지된다. filter 메서드의 경우 스트림의 크기가 변경될 수 있다. 따라서 SIZED 비트맵은 지우지만, 데이터 구조는 수정하지 않으므로 SORTED와 DISTINCT 비트맵은 유지한다.

    즉, 각 작업은 이전 단계의 비트맵과 변경된 비트맵을 결합하여 새로운 비트맵 set을 생성하게 된다. 이를 바탕으로 Apply 단계에서 일부 작업을 생략할 수 있다. 이를 이용하여 얻을 수 있는 성능 이점에 대해 아래 예제 코드로 살펴보자.

    public class StreamOptimization {
        public static void main(String[] args) {
            Set<Integer> set = new HashSet<>();
            Integer[] arr = new Integer[1_000_000];
    
            for (int i = 0; i < 1_000_000; i++) {
                set.add(i);
                arr[i] = i;
            }
    
            long start = System.currentTimeMillis();
    
    //        List<Integer> list = set.stream()
    //                .distinct()
    //                .collect(Collectors.toList());
    
            List<Integer> list = Arrays.stream(arr)
                    .distinct()
                    .collect(Collectors.toList());
    
            long end = System.currentTimeMillis() - start;
    
            System.out.println(end);
        }
    }
    
    첫번째 결과 : 41
    두번째 결과 : 163

    Set 컬렉션과 Integer 배열에 모두 같은 수를 넣어주고, 둘 다 중복값을 가지고 있지 않다. 그러나 둘의 스트림 연산을 비교해봤을 때 4배 정도의 성능 차이를 확인할 수 있다. 해당 결과가 나온 이유는 Set은 중복값을 절대 허용하지 않으므로 DISTINCT 비트맵이 1로 설정된다. 따라서 distinct 메서드가 실행되지 않는다. 그러나 Integer 배열은 특성상 중복값을 허용하므로 실제로는 중복값이 없더라도 DISTINCT 비트맵 값이 0으로 설정된다. 따라서 매번 distinct 메서드가 실행된다.

     

    댓글

Designed by Tistory.