본문 바로가기
카테고리 없음

Java - Stream 을 이용한 Group By 소개

by Nomangs 2022. 7. 6.
반응형

Java 에서 Stream 을 이용하면 다양한 동작을 가독성있게 코드 작성을 수행할 수 있습니다.

주로 여러개의 데이터에 쉽게 접근하기 위한 용도로 많이 사용합니다.

 

반복문부터.. 특정 데이터를 찾기 위한 것 뿐 아니라

오늘 다루게 될 Group 처리도 수행할 수 있습니다.

 

그리고 어떤 Stream 을 사용할지에 따라 동작 방식도 다르지만

오늘은 모두 생략하고 Grouping 하는 것에 대해서만 다루겠습니다.

 

 

groupingBy Collectors

Java 8 Stream API를 사용하면 선언적 방식으로 데이터 컬렉션을 처리할 수 있습니다.

정적 팩토리 메소드 Collectors.groupingBy() 및 Collectors.groupingByConcurrent() 는 SQL 언어 의 ' GROUP BY' 절 과 유사한 기능을 제공합니다 . 일부 속성으로 개체를 그룹화하고 Map 인스턴스에 결과를 저장하는 데 사용합니다.

 

groupingBy  의 오버로드된 메서드 는 다음과 같습니다.

먼저 분류 함수를 메서드 매개변수로 사용합니다.

static <T,K> Collector<T,?,Map<K,List<T>>> 
  groupingBy(Function<? super T,? extends K> classifier)


둘째, 분류 함수와 두 번째 수집기를 메서드 매개변수로 사용합니다.

static <T,K,A,D> Collector<T,?,Map<K,D>>
  groupingBy(Function<? super T,? extends K> classifier, 
    Collector<? super T,A,D> downstream)


마지막으로 분류 기능, 공급자 메서드(최종 결과를 포함하는 Map 구현 제공) 및 메서드 매개변수로 두 번째 수집기를 사용합니다.

static <T,K,D,A,M extends Map<K,D>> Collector<T,?,M>
  groupingBy(Function<? super T,? extends K> classifier, 
    Supplier<M> mapFactory, Collector<? super T,A,D> downstream)

예제 코드 설정

groupingBy() 의 사용법을 보여주기 위해 BlogPost 클래스 를 정의합시다 

( BlogPost 객체 의 Stream을 사용할 것입니다 )

class BlogPost {
    String title;
    String author;
    BlogPostType type;
    int likes;
}

enum BlogPostType {
    NEWS,
    REVIEW,
    GUIDE
}

그런 다음 BlogPost 개체를 생성합니다.

List<BlogPost> posts = Arrays.asList( ... );


단일 열에 의한 단순 그룹화

분류 함수만 매개변수로 사용하는 가장 간단한 groupingBy 메서드부터 시작하겠습니다. 스트림의 각 요소에는 분류 기능이 적용됩니다.

함수에서 반환된 값을 groupingBy 수집기 에서 가져온 맵의 키로 사용합니다 .
블로그 게시물 목록의 블로그 게시물을 유형별 로 그룹화하려면 다음을 수행합니다 .

Map<BlogPostType, List<BlogPost>> postsPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType));

복잡한 맵 키 유형 이 있는 groupingBy

분류 함수는 스칼라 또는 문자열 값만 반환하는 것으로 제한되지 않습니다. 결과 맵의 키는 필요한 equals 및 hashcode 메소드 를 구현하는 한 모든 객체가 될 수 있습니다 .

두 개의 필드를 키로 그룹화 하려면 javafx.util  또는  org.apache.commons.lang3.tuple  패키지 에서 제공하는 Pair 클래스 를 사용할 수 있습니다 .

예를 들어 Apache Commons Pair 인스턴스 에서 결합된 유형 및 작성자별로 목록의 블로그 게시물을 그룹화하려면 다음을 수행 합니다.

Map<Pair<BlogPostType, String>, List<BlogPost>> postsPerTypeAndAuthor = posts.stream()
  .collect(groupingBy(post -> new ImmutablePair<>(post.getType(), post.getAuthor())));

마찬가지로 이전에 정의한 Tuple 클래스를 사용할 수 있습니다. 이 클래스는 필요에 따라 더 많은 필드를 포함하도록 쉽게 일반화할 수 있습니다. Tuple 인스턴스를 사용하는 이전 예제는 다음과 같습니다.

Map<Tuple, List<BlogPost>> postsPerTypeAndAuthor = posts.stream()
  .collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor())));

Java 16에서는 변경할 수 없는 Java 클래스를 생성하는 새로운 형식으로 레코드 개념을 도입했습니다.
레코드 기능 은 Tuple보다 groupingBy  를 수행하는 더 간단하고 명확하며 안전한 방법을 제공합니다 . 예를 들어 BlogPost 에 레코드 인스턴스를 정의했습니다 .

public class BlogPost {
    private String title;
    private String author;
    private BlogPostType type;
    private int likes;
    record AuthPostTypesLikes(String author, BlogPostType type, int likes) {};
    
    // constructor, getters/setters
}

이제 레코드 인스턴스 를 사용하여 유형, 작성자 및 좋아하는 항목별로 목록 의 BlotPost 를 그룹화하는 것이 매우 간단 합니다.

Map<BlogPost.AuthPostTypesLikes, List<BlogPost>> postsPerTypeAndAuthor = posts.stream()
  .collect(groupingBy(post -> new BlogPost.AuthPostTypesLikes(post.getAuthor(), post.getType(), post.getLikes())));

여러 필드로 그룹화

 

BlogPost 목록 을 먼저 작성자 별로 그룹화 한 다음 유형별 로 그룹화하려면 다음을 수행 하십시오.

Map<String, Map<BlogPostType, List>> map = posts.stream()
  .collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType)));

그룹화된 결과에서 평균 얻기

예를 들어 각 블로그 게시물 유형 에 대한 평균 좋아요 수를 찾으려면 다음을 수행합니다 .

Map<BlogPostType, Double> averageLikesPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType, averagingInt(BlogPost::getLikes)));

그룹화된 결과에서 합계 가져오기

각 유형 에 대한 총 좋아요 수를 계산하려면 다음을 수행합니다 .

Map<BlogPostType, Integer> likesPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType, summingInt(BlogPost::getLikes)));

그룹화된 결과에서 최대값 또는 최소값 가져오기

우리가 수행할 수 있는 또 다른 집계는 최대 좋아요 수가 있는 블로그 게시물을 가져오는 것입니다.

Map<BlogPostType, Optional<BlogPost>> maxLikesPerPostType = posts.stream()
  .collect(groupingBy(BlogPost::getType,
  maxBy(comparingInt(BlogPost::getLikes))));

마찬가지로 minBy 다운스트림 수집기를 적용하여 최소 좋아요 수의 블로그 게시물을 얻을 수 있습니다.
maxBy 및 minBy 수집기는 적용되는 컬렉션이 비어 있을 가능성을 고려합니다 . 이것이 지도의 값 유형이 Optional<BlogPost> 인 이유 입니다.

반응형

댓글