ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - 스트림(Stream) 최종 처리 메서드의 종류와 사용 방법(3)
    Language/Java 2022. 12. 1. 16:03

    스트림(Stream) 최종 처리 메서드의 종류와 사용 방법(3)

    요소를 그룹핑해서 수집

    저번 시간에는 그룹핑을 제외한 스트림의 수집 방식에 대해 알아보았다. 이번 게시글에서는 collect() 메서드를 이용해서 그룹핑하는 방법을 알아보겠다.

    collect() 메서드는 단순히 요소를 수집하는 기능 이외에 컬렉션의 요소들을 그룹핑해서 Map 객체를 생성하는 기능도 제공한다. 여기서 collect() 메서드를 호출할 때 매개 변수로 groupingBy() 또는 groupingByConcurrent() 메서드를 사용하면 된다.

     

     

    첫 번째는 매개변수로 Function<T, K> classifier를 사용하는 groupingBy() 메서드이다. 이 메서드의 리턴 타입을 보면, Collector<T, ?, Map<K, List<T>>>로, T를 K로 매핑한 후, 키가 K이면서 T를 저장하는 요소를 값으로 갖는 Map을 생성한다.

     

    두 번째는 매개변수로 Function<T, K> classifier와 Collector<T, A, D> downstream을 사용하는 groupingBy() 메서드이다. 이 메서드의 리턴 타입을 보면, Collector<T, ?, Map<K, D>>로 T를 K로 매핑한 후, 키가 K이면서 키에 저장된 D 객체에 T를 누적한 Map을 생성한다.

     

    마지막은 이전에 설명한 메서드에 Supplier 매개 변수가 추가된 형태이다. 사용 방식 또한 위와 유사하다. 그냥 Map이 아닌 TreeMap과 같은 Supplier가 제공하는 Map을 사용한다.

     

    그룹핑은 이해가 어렵기 때문에, 예시 코드를 살펴보도록 하겠다.

     

    public class Main {
        public static void main(String[] args) {
            List<Student> studentList = Arrays.asList(
                    new Student("이순신", Gender.MALE, 95, City.Seoul),
                    new Student("홍길동", Gender.FEMALE, 85, City.Pusan),
                    new Student("임꺽정", Gender.MALE, 91, City.Pusan),
                    new Student("이강인", Gender.FEMALE, 84, City.Seoul)
            );
    
            Map<Gender, List<Student>> mapByGender = studentList.stream()
                    .collect(Collectors.groupingBy(s -> s.getGender()));
    
            System.out.println("===== 남학생 =====");
            mapByGender.get(Gender.MALE).forEach(s -> System.out.println(s.getName() + " "));
    
            System.out.println("===== 여학생 =====");
            mapByGender.get(Gender.FEMALE).forEach(s -> System.out.println(s.getName() + " "));
        }
    }

    위 예시 코드는 첫 번째 groupingBy() 메서드를 사용한 것이고, 학생의 성별을 기준으로 그룹핑하였다. groupingBy() 메서드의 매개 변수로는 Function<T, K>를 사용하여 T를 K로 매핑하였다. 이때, T는 Student이고, K는 Gender이므로 Map<Gender, List<Student>>이 생성되는 것이다.

     

    ===== 남학생 =====
    이순신 
    임꺽정 
    ===== 여학생 =====
    홍길동 
    이강인

    출력 결과는 위와 같다.

     

    public class Main {
        public static void main(String[] args) {
            List<Student> studentList = Arrays.asList(
                    new Student("이순신", Gender.MALE, 95, City.Seoul),
                    new Student("홍길동", Gender.FEMALE, 85, City.Pusan),
                    new Student("임꺽정", Gender.MALE, 91, City.Pusan),
                    new Student("이강인", Gender.FEMALE, 84, City.Seoul)
            );
    
            Map<City, List<String>> mapByCity = studentList.stream()
                    .collect(Collectors.groupingBy(
                            x -> x.getCity(),
                            Collectors.mapping(s -> s.getName(), Collectors.toList())
                    ));
    
            System.out.println("===== 서울 =====");
            mapByCity.get(City.Seoul).forEach(s -> System.out.println(s));
    
            System.out.println("===== 부산 =====");
            mapByCity.get(City.Pusan).forEach(s -> System.out.println(s));
        }
    }

    위 예시 코드는 두 번째 groupingBy() 메서드를 사용한 것이고, 학생의 지역을 기준으로 그룹핑하였다. groupingBy() 메서드의 매개변수로는 Function<T, K>와 Collector<T, A, D>을 사용하였다.

     

    ===== 서울 =====
    이순신
    이강인
    ===== 부산 =====
    홍길동
    임꺽정

    실행결과는 위와 같다.

     

    그룹핑 후 매핑 및 집계

     

    리턴 타입 메서드(매개 변수) 설명
    Collector<T, ?, R> mapping<Function<T, U> mapper, Collector<U, A, R> collector>) T를 U로 매핑한 후, U를 R에 수집
    Collector<T, ?, Double> averagingDouble(toDoubleFunction<T> mapper) T를 Double로 매핑한 후, Double의 평균값을 산출
    Collector<T, ?, Long> counting() T의 카운팅 수를 산출
    Collector<CharSequence, ?, String> joining(CharSequence delimiter) CharSequence를 구분자로 연결한 String을 산출
    Collector<T, ?, Optional<T>> maxBy(Comparator<T> comparator) Comparator를 이용해서 최대 T를 산출
    Collector<T, ?, Optional<T>> minBy(Comparator<T> comparator) Comparator를 이용해서 최소 T를 산출
    Collector<T, ?, Integer> summingInt(ToIntFunction function) int 타입의 합계 산출
    Collector<T, ?, Long> summingLong(ToLongFunction function) long 타입의 합계 산출
    Collector<T, ?, Double> summingDouble(ToDoubleFunction function) double 타입의 합계 산출

    Collectors.groupingBy() 메서드는 그룹핑 후, 매핑이나 집계를 할 수 있도록 두 번째 매개 변수로 Collector를 가질 수 있다. 위 예제에서 매개 변수가 하나만 있는 groupingBy()  메서드도 있었지만, 2개 또는 3개가 있는 groupingBy() 메서드도 있었다.

    이 중, 2개 또는 3개의 매개 변수를 갖는 groupingBy() 메서드가 그룹핑 후 매핑 및 집계를 사용한다. 아래 예제에서는 mapping() 메서드를 활용하였지만, 집계를 통해서 value를 무궁무진하게 변화시킬 수 있다.

     

    public class Main {
        public static void main(String[] args) {
            List<Student> studentList = Arrays.asList(
                    new Student("이순신", Gender.MALE, 95, City.Seoul),
                    new Student("홍길동", Gender.FEMALE, 85, City.Pusan),
                    new Student("임꺽정", Gender.MALE, 91, City.Pusan),
                    new Student("이강인", Gender.FEMALE, 84, City.Seoul)
            );
    
            Map<Gender, Double> mapByGender = studentList.stream()
                    .collect(
                            Collectors.groupingBy(
                                    x -> x.getGender(),
                                    Collectors.averagingDouble(x -> x.getScore()))
                    );
    
            System.out.println("남학생 평균 점수 : " + mapByGender.get(Gender.MALE));
            System.out.println("여학생 평균 점수 : " + mapByGender.get(Gender.FEMALE));
        }
    }
    

     

    위 코드는 학생들을 성별을 기준으로 같은 그룹에 속하는 학생들의 평균 점수를 저장하는 맵을 생성 한다.

     

    남학생 평균 점수 : 93.0
    여학생 평균 점수 : 84.5

    실행 결과는 위와 같다.

     

    public class Main {
        public static void main(String[] args) {
            List<Student> studentList = Arrays.asList(
                    new Student("이순신", Gender.MALE, 95, City.Seoul),
                    new Student("홍길동", Gender.FEMALE, 85, City.Pusan),
                    new Student("임꺽정", Gender.MALE, 91, City.Pusan),
                    new Student("이강인", Gender.FEMALE, 84, City.Seoul)
            );
    
            Map<Gender, String> mapByName = studentList.stream()
                    .collect(
                            Collectors.groupingBy(
                                    x -> x.getGender(),
                                    Collectors.mapping(
                                            x -> x.getName(),
                                            Collectors.joining(", ")
                                    )
                            )
                    );
    
            System.out.println("남학생 이름 : " + mapByName.get(Gender.MALE));
            System.out.println("여학생 이름 : " + mapByName.get(Gender.FEMALE));
        }
    }

    위 예제 코드는 학생들을 성별로 그룹핑한 다음 같은 그룹에 속하는 학생 이름을 쉼표로 구분해서 문자열을 만들고, 성별을 key로 문자열을 value로 갖는 Map을 생성한다.

    mapping() 메서드를 통하여 Student를 String으로 매핑한다. 여기서 유의해야 할 점은 구분자(,)는 맨 마지막에는 추가되지 않는다는 것이다.

     

    남학생 이름 : 이순신, 임꺽정
    여학생 이름 : 홍길동, 이강인

    실행 결과는 위와 같다.


    출처

    https://steady-coding.tistory.com/318

     

    728x90

    댓글

Designed by Tistory.