ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스트림 소개
    Java/모던자바인액션요약 2022. 8. 15. 00:01
    728x90

    스트림 이전의 세상

    자바에서는 Collection을 만들고 처리하는 과정을 포함합니다.

    대부분의 데이터베이스들은 다음과 같은 명령어를 통해 해결합니다.

    SELECT name FROM dishes WHERE calorie < 400

    하지만 자바 컬렉션은 위와 같이 처리하지 못하고 컬렉션의 각 요소를 반복하면서 모든 컬렉션의 값을 비교하는 방식으로 사용해야 합니다.

     

    List<String> result = new ArrayList<>();
    for(Dish cur : dishes){
    	if(cur.calorie < 400){
    		result.add(cur.name);
        }
    }

     

    또한 성능을 높이기 위해 멀티코어 아키텍처를 활용하려면 병렬로 컬렉션 요소를 처리해야 합니다.

    이때 코드는 복잡해지고 디버깅도 어려워집니다.

     

     

    스트림의 등장

    스트림은 자바 8에 새로 추가된 기능입니다.

    영어로는 시냇물이라는 뜻으로 물이 흘러가듯이 데이터가 흘러감을 의미합니다.

     

    장점 1

    스트림을 이용하면 데이터베이스에서 처리하는 것과 같이 선언형으로 컬렉션 데이터를 처리할 수 있습니다.

    (여러 컬렉션을 스트림을 통해 한번에 처리할 수 있는 표준화의 측면도 존재)

     

    장점 2

    멀티코어 아키텍처를 활용하기 위해 어려운 작업이 존재하지 않고 간단하게 활용할 수 있습니다.

     

    위의 코드를 스트림을 활용하여 작성

    List<String> lowCaloricDishName = dishes.stream()
                                                    .filter(d -> d.getCalories() < 400)
                                                    .map(Dish::getName)
                                                    .collect(toList());

     

    filter 메서드를 통하여 칼로리가 400 이하인 음식들만 추출이 됩니다.

    map 메서드를 통해서 음식의 이름을 추출합니다.

    이후에 toList() 메서드를 통해 List로 만들어서 반환합니다.

     

    filter, map, collect와 같은 연산을 통해 연산을 파이프라인으로 연결해도 가독성과 명확성이 유지됩니다.

     

    또한 병렬적으로 처리하고 시다면 stream을 paralleStream으로만 변환하면 해결할 수 있습니다.

    즉, 스레드와 락을 걱정하기 않고 병렬화 할 수 있음.

     

     

    스트림의 특징

    파이프라이닝

    스트림 연산끼리 연결하여 커다란 파이프라인을 구성할 수 있도록  스트림 연산은 스트림 자신을 반환합니다.

    그 덕분에 Lazy 연산, 쇼트서킷과 같은 최적화를 얻을 수 있습니다.

     

    내부 반복

    반복자를 이용하여 명시적으로 반복하는 컬렉션과 다르게 내부 반복을 지원합니다.

     

    일회용

    최종 연산을 통해 단 한 번만 소비할 수 있다

    재활용 불가능

     

    위의 특징들에 대해서 아래에서 조금 더 자세하게 다룹니다.

     

    스트림의 컬렉션의 가장 큰 차이

    스트림과 컬렉션의 차이는 데이터를 언제 계산하느냐에 달려있습니다.

     

    컬렉션은 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조입니다.

    즉, 컬렉션에 추가되기 전에 연산이 일어납니다.

     

    스트림은 이론적으로 요청할 때만 요소를 계산하는 고정된 자료구조입니다.

    즉, 사용자가 요청하는 값만 스트림에서 추출합니다.

    다른 말로 게으르게 만들어지는 컬렉션이라고도 함.

     

    외부 반복과 내부 반복

    컬렉션은 외부 반복으로 명시적으로 컬렉션 항목을 하나씩 가져와서 처리합니다.

    내부 반복은 여러 개의 항목을 동시에 처리하는 병렬성과 더 최적화된 다양한 순서로 처리할 수 있습니다.

     

    중간 연산과 최종 연산

    filter나 sorted 같은 중간 연산은 다른 스트림을 반환합니다.

    이를 통해 스트림의 특징인 파이프라이닝을 구성할 수 있습니다.

    중간 연산의 중요한 특징은 최종 연산까지 도달하기 전까지는 아무 연산도 수행하지 않는 Lazy 한 특성을 가집니다.

     

    이런 Lazy한 특성 때문에 limit 연산 그리고 쇼트서킷이라고 불리는 기법으로 중간 연산이 무한한 범위를 가지는 경우에도 유한한 시간 내에 연산이 완료될 수 있습니다.

     

    쇼트서킷이란 논리 연산에서 두 피연산자 중 어느 한쪽만 '참'이면 우측 피연산자의 값은 평가하지 않고 바로 결과를 얻는 행위입니다.

     

    스트림에서는 allMatch, anyMatch, noneMatch, findFirst, findAny 등의 쇼트서킷 연산을 지원합니다.

     

    예를 들어 while(true){ count++} 같은 경우에는 외부 반복으로 명시적으로 값을 가져오기 때문에 계속 반복되지만 여기에 스트림을 도입하여 limie(3) 같은 연산을 주게 되면 count=1 일 때 1,2,3과 같은 값을 얻을 수 있습니다.

     

    최종 연산은 연산 결과가 스트림이 아닌 최종 결과를 도출합니다.

    보통 최종 연산을 통해 List, Integer, void 등 스트림 이외의 결과가 반환됩니다.

    댓글

Designed by Tistory.