Books/Modern Java In Action

[모던 자바 인 액션] Chapter4. 스트림 소개

Sol b 2023. 7. 26. 14:28

스트림은 자바 8 API에 새로 추가된 기능입니다.
스트림을 이용하면 선언형(코드 대신 질의로 표현)으로 컬렉션 데이터를 처리할 수 있습니다.

간단하게 컬렉션 반복은 멋있게 처리하는 놈이라고 생각하면 됩니다!
  왜나하면 멀티스레드 코드를구현하지 않아도 데이터를 병렬로 처리할 수 있기 때문입니다  

그럼 바로 for문과 스트림을 비교하는 코드를 작성해 보겠습니다.

저칼로리의 요리명을 반환하고, 칼로리를 기준으로 요리를 정렬하는 예제입니다.

 

For 반복문으로

public class HighCaloriesNames_Forloop {
    public static void main(String[] args) {
        final List<Dish> dishes = Dish.menu;
        List<Dish> lowCaloricDishes = new ArrayList<>();

        // 칼로리 필터링
        for (Dish dish : dishes) {
            if (dish.getCalories() < 400) {
                lowCaloricDishes.add(dish);
            }
        }

        // 정렬 (익명클래스 사용)
        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
            @Override
            public int compare(final Dish o1, final Dish o2) {
                return o1.getCalories() - o2.getCalories();
            }
        });

        // 이름 필터링
        List<String> lowCaloricDishesName = new ArrayList<>();
        for (Dish dish : lowCaloricDishes) {
            lowCaloricDishesName.add(dish.getName());
        }

        //출력
        System.out.println(lowCaloricDishesName);
    }
}

결과

[season fruit, rice]

 

가독성이 좋지 않고 코드의 양도 너무 길다고 느껴집니다.

그럼이제 스트림을 통해 예제를 풀어보겠습니다.

 

스트림

public class HighCaloriesNames_Stream {
    public static void main(String[] args) {
        final List<Dish> dishes = Dish.menu;
        List<String> lowCaloricDishesName =
                dishes.stream()
                        .filter(d -> d.getCalories() < 400) // 칼로리 필터링
                        .sorted(comparing(Dish::getCalories)) // 정렬
                        .map(Dish::getName)
                        .toList();
        
        System.out.println(lowCaloricDishesName);
    }
}

스트림을 사용하니 가독성이 매우 좋아졌고 코드의 양 또한 현저히 줄었습니다.

이제 협업을 할때나 유지보수를 할때 스트림이 매우 유용하다는 것은 명확한 사실이겠죠?

 

스트림은 병렬로도 처리가능하다고 앞서 말씀드렸는데요

간단히 Stream()을 parallelStream()으로 바꿔주기만 하면 됩니다!

 

병렬 스트림

public class HighCaloriesNames_Stream {
    public static void main(String[] args) {
        final List<Dish> dishes = Dish.menu;
        List<String> lowCaloricDishesName =
                dishes.parallelStream()
                        .filter(d -> d.getCalories() < 400) // 칼로리 필터링
                        .sorted(comparing(Dish::getCalories)) // 정렬
                        .map(Dish::getName)
                        .toList();

        System.out.println(lowCaloricDishesName);
    }
}

이렇게 쉽게 안전한 병렬 처리가 가능합니다!

 

정확히 얼마나 성능이 좋은지, 얼마나 많은 스레드가 사용되는지 등등 병렬 스트림에 관해서는 추후에 더 다루겠습니다.

 

지금은 스트림의 기능이 소프트웨어공학적으로 다음의 다양한 이득은 준다는 사실만 알면 됩니다! 

  • 선언형으로 코드를 구현할 수 있다.
    • 루프, if 조건문 등의 제어블록을 사용하지 않고 "저칼로리의 요리만 선택하라"  같은 질의 동작수행 가능
  •  선언형 코드와 동작 파라미터화를 활용해 변하는 요구사항에 쉽게 대응할 수 있다. 
    • 기존코드를 복사/붙여넣기  방식이 아닌 람다식을 활용해  요구사항을 쉽게 구현 가능
  • filter, sorted, map, collect 같은 연산을연결해 파이프라인으로 만들 수 있다.
  • 병렬화하여도 스레드와 락을걱정할 필요가 없습니다.
    • 스트림 함수는 고수준 빌딩 블록으로 스레딩 모델에 제한되지 않습니다.   

 

스트림 장점

스트림의 장점은 3가지로 요약할 수 있습니다.

  • 선언형 : 더 간결하고 가독성이 좋아진다.
  • 조립할 수 있음 : 유연성이 좋아진다.
  • 병렬화 : 성능이 좋아진다.

 

느낀점

확실히 반복문보다는 스트림이 가독성이 좋고 편리한 것을 느꼈다.

좋은 개발자가 되기위한 필수 스킬이니 꼭 마스터 하겠다!