Stream API를 활용한 리스트 그룹화 및 합계 통계 (2가지 방식)

1. String 타입 필드 기준 그룹화 후 BigDecimal 합계 구하기

1.1 원본 데이터와 요구사항

요구사항: 출발지와 도착지가 동일한 데이터는 합쳐서 하나로 표현하고, 나머지 두 수치 필드는 합산한다.

원본 데이터 예시:

원본 데이터 이미지

1.2 Stream API를 이용한 리스트 처리

코드는 다음과 같다:

    @Override
    public Map<String, Object> countCarLine(String begin, String end) {
        // 라인 통계
        List<CarLineVO> carLineList = dashboardMapper.countCarLine(begin, end);

        // 처리된 데이터를 저장할 리스트
        List<CarLineVO> mergedCarLineList = new ArrayList<>();

        // 그룹화 및 합산 처리
        carLineList.parallelStream()
                .collect(Collectors.groupingBy(
                    item -> item.getDeliverAddress() + item.getCollectAddress(),
                    Collectors.toList()))
                .forEach((key, groupList) -> {
                    groupList.stream()
                            .reduce((a, b) -> new CarLineVO(
                                a.getDeliverAddress(),
                                a.getCollectAddress(),
                                a.getCollectNetWeight().add(b.getCollectNetWeight()),
                                a.getTotalFreightPrice().add(b.getTotalFreightPrice())))
                            .ifPresent(mergedCarLineList::add);
                });

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("carLine", mergedCarLineList);
        return resultMap;
    }

1.3 처리 후 요구사항에 맞는 데이터

처리된 데이터 예시:

처리된 데이터 이미지

1.4 엔티티 클래스

CarLineVO 클래스:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CarLineVO {
    private String deliverAddress;
    private String collectAddress;
    private BigDecimal collectNetWeight;
    private BigDecimal totalFreightPrice;
}

엔티티는 Lombok 플러그인을 사용한다.

2. String 타입 필드 기준 그룹화 후 int 타입 합계 구하기

2.1 엔티티 클래스

package com.qs.modules.formMaterial.model;

import com.baomidou.mybatisplus.annotation.*;
import com.qs.db.base.model.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("form_material")
@ApiModel(value = "신청서와 자재 중간 테이블", description = "신청서와 자재 중간 테이블")
public class FormMaterial extends BaseModel {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "기본키 id")
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;

    @ApiModelProperty(value = "신청서 id")
    private String applicationFormId;

    @ApiModelProperty(value = "신청 자재 id")
    private String materialId;

    @ApiModelProperty(value = "신청 자재 수량")
    private int materialCount;

    @ApiModelProperty(value = "출고된 자재 총 수량")
    private int takeTotalCount;

    @ApiModelProperty(value = "잔여 자재 수량")
    private int remainingCount;

    @ApiModelProperty(value = "자재 수령 완료 여부 (0: 미완료, 1: 완료)")
    private String materialFlag;

    @TableField(exist = false)
    private String materialNumber;

    @TableField(exist = false)
    private String materialName;

    @TableField(exist = false)
    private String queryMaterialCount;

    public static final String APPLICATION_FORM_ID = "application_form_id";
    public static final String MATERIAL_COUNT = "material_count";
}

2.2 요구사항

입력 데이터는 다음과 같으며, materialId가 동일한 항목은 materialCount를 합산해야 한다.

[
    {
        "materialId": "1453637685838372866",
        "materialCount": 15
    },
    {
        "materialId": "1453638065183809538",
        "materialCount": 21
    },
    {
        "materialId": "1482879355628838914",
        "materialCount": 8
    }
]

2.3 Stream 처리

        // 중복된 자재 정보를 물질 ID 기준으로 그룹화하여 수량 합산 후 하나의 항목으로 병합
        // formMaterialList는 원본 리스트
        List<FormMaterial> mergedList = new ArrayList<>();
        formMaterialList.parallelStream()
            .collect(Collectors.groupingBy(FormMaterial::getMaterialId, Collectors.toList()))
            .forEach((materialId, groupList) ->
                groupList.stream()
                    .reduce((a, b) ->
                        new FormMaterial()
                            .setMaterialId(a.getMaterialId())
                            .setMaterialCount(a.getMaterialCount() + b.getMaterialCount()))
                    .ifPresent(mergedList::add));

3. 요약

이 방식은 기존의 for 루프와 유사하게 느껴지지만, 스트림 연산 내에서도 .forEach를 사용하여 반복 처리를 수행한다는 점이 특징이다.

태그: Stream API java List 그룹화 Collectors.groupingBy BigDecimal 합계

6월 23일 21:08에 게시됨