모던 자바 인 액션 6장 1

모던 자바 인 액션 6장 1

2021, Mar 12    

모던 자바 인 액션 6장.

스트림으로 데이터 수집

스트림이란 데이터 집합을 멋지게 처리하는 게으른 반복자

  • 중간 연산
    • filter, map
  • 최종 연산
    • count, findFirst, forEach, reduce

리듀싱과 요약

  • 다음과 같은 클래스와 리스트가 준비 되어 있다.

    public class Dish {
      
        private final String name;
        private final int calories;
      
        public Dish(final String name, final int calories) {
            this.name = name;
            this.calories = calories;
        }
      
        public String getName() {
            return name;
        }
      
        public int getCalories() {
            return calories;
        }
    }
      
    public class Main {
      public static void main(String[] args) {
        List<Dish> menu = new ArrayList<>();
        // 이곳에서 예시 작업을 진행
      }
    }
    
  • 스트림값에서 최대값과 최소값 검색

    Optional<Dish> mostCalories = menu.stream()
                    .collect(Collectors.minBy(Comparator.comparingInt(Dish::getCalories)));
    
  • 요약 연산 평균값 구하기

            double averageCalories = menu.stream()
                    .collect(Collectors.averagingDouble(Dish::getCalories));
    
  • 요약 연산 데이터 수집

    public class Main {
        public static void main(String[] args) {
            List<Dish> menu = Arrays.asList(
                    new Dish("pork", 800),
                    new Dish("beef", 700),
                    new Dish("salmon", 450),
                    new Dish("bean", 120)
            );
            IntSummaryStatistics menuStatistics = menu.stream()
                    .collect(Collectors.summarizingInt(Dish::getCalories));
            System.out.println(menuStatistics);
        }
    }
      
    // 출력 결과
    // IntSummaryStatistics{count=4, sum=2070, min=120, average=517.500000, max=800}
    
  • 문자열 연결

            String shortMenu = menu.stream()
                    .map(Dish::getName) // toString() 메서드가 포함되어 있으면 이 부분 생략 가능
                    .collect(Collectors.joining(","));
            System.out.println(shortMenu);
              
            // 출력 결과
            // pork,beef,salmon,bean
    
  • 범용 리듀싱 요약 연산

    int totalCalories1 = menu.stream()
                    .collect(Collectors
                             .reducing(0, Dish::getCalories, (i, j) -> i + j));
    int totalCalories2 = menu.stream()
                    .map(Dish::getCalories)
                    .reduce(0, Integer::sum);
    // 첫 번째 인수 0은 인수가 없을 때 반환값이다.
    // 두 번째 인수는 칼로리를 정수로 변환할 때 사용하는 함수다.
    // 세 번째 인수는 두 항목을 하나로 더하는 BinaryOperator 다.
      
    /* collect 메서드는 도출하려는 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드인 반면, reduce 는 두 값을 하나로 도출하는 불변형 연산이라는 점에서 의미론적인 문제가 일어난다.
    가변 컴테이너 관련 작업이면서 병렬성을 확보하려면 collect 메서드로 리듀싱 연산을 구현하는 것이 바람직하다.*/
    
    • 하지만 reduce 는 박싱, 언박싱 비용이 들어가서 for-loop 보다 느리다.

    • 그렇기 때문에 IntStream 에서 제공하는 메서드가 있으면 그것을 사용하는 것이 더 유용하다.

      int totalCalories3 = menu.stream().mapToInt(Dish::getCalories).sum();
      
  • (응용) 가장 높은 칼로리 찾기

    Optional<Dish> maxCalories = menu.stream()
                    .reduce((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2);
    

Collect ? Stream API 에서 제공하는 메서드 ? 어떤걸 사용하지?

  • 컬렉터를 사용하는 것이 Stream API 에서 제공하는 메서드를 이용하는 것보다 코드가 더 복잡하다.
  • 하지만 재사용성과 커스터마이즈 가능성을 제공하는 높은 수준의 추상화와 일반화를 얻을 수 있다.