반응형

groupingBy

groupingBy 는 Java Stream collect 메서드에서 사용하는 Collector 객체이자 특정 속성(property) 값에 의해서 그룹핑을 짓는 메서드이다. 결과값으로 항상 Map<K, V> 형태를 리턴하며 아래와 같이 최대 3가지 파라미터를 받을 수 있다. 두 가지 인수만 사용된다면 classifier 와 downstream 만 사용한다.

  1. classifier (Function<? super T, ? extends K>): 분류 기준을 정의한 함수
  2. mapFactory (Supplier<M>): 결과 값 Map<K,V> 를 다른 Object 로 맵핑하는 함수
  3. downStream (Collector<? super T,A,D>): 집계 방식을 변경하는 또 다른 Collector 객체로 결과 값 Map<K,V> 에서 V 의 타입을 변경

자세한 사항은 아래 공식 DOCS 문서를 참고한다.

Collectors (Java Platform SE 8 ) (oracle.com)

 

Collectors (Java Platform SE 8 )

Returns a Collector implementing a "group by" operation on input elements of type T, grouping elements according to a classification function, and returning the results in a Map. The classification function maps elements to some key type K. The collector p

docs.oracle.com

 

groupingBy 예제

아래와 같이 Person 타입이 있다고 가정할 때 다양한 groupingBy 예제를 살펴보고 정리한다.

@AllArgsConstructor
@Setter
public static class Person {
        private String name;
        private String city;
        private Integer age;
        private PersonJob personJob;
}

public static class PersonTuple {
        private String name;
        private String city;

        public PersonTuple(String name, String city) {
            this.name = name;
            this.city = city;
        }
}

 

private static List<Person> getPersons() {
        return List.of(
            new Person("안유진", "서울", 20, PersonJob.ARMY),
            new Person("리즈", "서울", 22, PersonJob.STUDENT),
            new Person("레이", "도쿄", 19, PersonJob.STUDENT),
            new Person("가을", "수원", 18, PersonJob.EMPLOYEE),
            new Person("장원영", "수원", 20, PersonJob.POLICE),
            new Person("이서", "청주", 18, PersonJob.POLICE)
        );
}

 

단일 키로 그룹핑하기

var result = getPersons().stream().collect(groupingBy(Person::getCity));

Map<String, List<Person>> 객체로 리턴되고, 수원, 서울, 청주, 도쿄 키로 Person 객체로 그룹핑된다.

 

복합 키로 그룹핑하기

public static class PersonTuple {
        private String name;
        private String city;

        public PersonTuple(String name, String city) {
            this.name = name;
            this.city = city;
        }
}
var result2 = getPersons().stream().collect(groupingBy(person -> new PersonTuple(person.getName(), person.getCity())));

Map<PersonTuple, List<Person>> 객체로 리턴되고, 위에 정의된 Tuple 로 Person 객체가 그룹핑된다.

 

집계 변경해서 그룹핑하기 (toSet)

var result3 = getPersons().stream().collect(groupingBy(Person::getPersonJob, toSet()));

groupingBy 를 적용했을 때 기본 Value 타입은 리스트다. 위 예제처럼 toSet 로 집계함수를 변경하면,

Map<Person, Set<Person>> 객체로 리턴된다.

 

집계 변경해서 통계 그룹핑하기 (sum)

단순히 타입을 변경하는 것 이외에 reduce 같은 함수로 통계 데이터를 얻어올 수 있다.

var result4 = getPersons().stream().collect(groupingBy(Person::getPersonJob, summingInt(Person::getAge)));

downStream 방식을 summingInt 메서드로 합계를 낸 예제이다. 직업을 기준으로 Person 객체를 그룹핑했을 때 그 객체 나이들을 전부 더해서 산출한다. 리턴 타입은 Map<Person, Integer>> 이다.

 

Map 의 Value 값을 다른 타입으로 리턴하기

var result5 = getPersons().stream().collect(groupingBy(Person::getPersonJob, mapping(Person::getName, joining(",", "[", "]"))));

groupingBy 의 downStream 을 통해 Map 의 value 값을 다른 타입으로 리턴할 수 있다. 위 처럼 기본 컬렉션 타입은 toSet, toList, toMap, toConcurrentMap, toCollection 으로 static 메서드를 제공하지만 그 이외의 타입은 mapping 메서드를 통해 다른 타입을 변경할 수 있다. mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream) 원형을 살펴보면 groupingBy 의 2개 인자를 받는 함수 원형과 같다. 똑같이 Key 를 분류하는 함수와 value 를 집계하기 위한 함수를 제공하면 된다.

 

Map<PersonJob, String> 객체로 리턴된다. Value 가 집계될 때 "[" "]" 로 감싸서 값들을 , 콤마로 묶는다.

 

Map 을 다른 타입으로 리턴하기

var result6 = getPersons().stream().collect(groupingBy(Person::getPersonJob, () -> new EnumMap<>(PersonJob.class), toList()));

EnumMap<PersonJob, List<Person>> 객체로 리턴된다. 세 가지 인수를 받을 때는 두 번째 인자가 MapFactory 이고 세 번째 인자가 downstream 이다. mapFactory 가 Supplier 이므로 인자가 없는 함수형 인터페이스를 제공해야 하므로 () -> new 형식으로 새로운 Map 타입으로 리턴하면 된다.

반응형

'JAVA' 카테고리의 다른 글

[JAVA] 타입 토큰과 슈퍼 타입 토큰이란?  (0) 2022.10.10

+ Recent posts