簡體   English   中英

將流分組為 POJO 的正確方法

[英]Right way to group a stream into POJOs

我有一個匯總行列表,其中每個實體的行數很少,其中重復了實體的一些標量屬性,並且還有兩個唯一的附加列 GroupName 和 GroupCount。

基本上這是一個 SQL 連接的輸出,實體數據是重復的,並且有一個唯一的組名,以及它在每行中的計數。

我想流式傳輸它並將其收集到一個實體 Dto 中,該實體具有實體屬性以及合並組統計信息的 Map。

我嘗試了使用 Collectors.groupingBy 的實現,但它看起來仍然不正確。

    @Data
    @AllArgsConstructor
    public static class DepartmentSummaryRow{
        private int id;
        private String name;
        private String groupName;
        private int groupMembersCount;
    }
    @Data
    @AllArgsConstructor
    public static class Department{
        private int id;
        private String name;
        @EqualsAndHashCode.Exclude
        private final Map<String, Integer> groupCounts = new HashMap<>();
    }
    
    public static void main(String[] args) {
        grouping();
    }
    
    private static void grouping() {
        Gson g = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
        
        //Test data
        List<DepartmentSummaryRow> summaries = new ArrayList<>();
        for(int i=1;i<=50;i++) {
            summaries.add( new DepartmentSummaryRow(i, "name_a"+i, "g1", 3 ) );
            summaries.add( new DepartmentSummaryRow(i, "name_b"+i, "g2", 9 ) );
        }
        

        //Just group the summary rows
        Map<Department, List<DepartmentSummaryRow>> departmentsToSummaries = summaries
                                                                    .stream()
                                                                    .collect(
                                                                            Collectors.groupingBy( 
                                                                                    (summary)->{ return new Department(summary.id, summary.name); }, 
                                                                                    LinkedHashMap::new, 
                                                                                    Collectors.toList()
                                                                            )
                                                                    );
        
        //Merge the info into the departments
        departmentsToSummaries.forEach( (entity, sumaryRow)->{ 
            entity.groupCounts.putAll( 
                    sumaryRow.stream().collect( 
                                Collectors.groupingBy( 
                                    DepartmentSummaryRow::getGroupName, 
                                    Collectors.summingInt( DepartmentSummaryRow::getGroupMembersCount ) 
                                ) 
                            ) 
                ) ;
            } );
        
        System.out.println( g.toJson( departmentsToSummaries.keySet() ) );
    }

我正在尋找比這更好的實現的一些想法,以便將流分組為自定義 POJO。 任何的意見都將會有幫助。 謝謝!

(注意:這本身有一些錯誤......出於某種原因,我的 POJO 的第一個分組根本沒有分組......這很奇怪,因為它有一個很好的哈希碼和 Lombok 提供的 equals)

編輯:這是輸入的樣子:

[
  { "id": 1, "name": "name_a1", "groupName": "g1", "groupMembersCount": 3 }, 
  { "id": 1, "name": "name_b1", "groupName": "g2", "groupMembersCount": 9 }, 
  { "id": 2, "name": "name_a1", "groupName": "g1", "groupMembersCount": 3 }, 
...
]

這是預期的結果:

[ 
  { "id": 1, "name": "name_a1", "groupCounts": { "g1": 3, "g2": 9 } }, 
  { "id": 2, "name": "name_a2", "groupCounts": { "g1": 3, "g2": 9 } },
...
]

主要問題是,只有通過summary.id進行分組( summary.name值不同)才能檢索預期結果,然后第一個匹配的DepartmentSummaryRow的名稱應該應用於剩余的Department

因此,從Department equalshashCode中排除name的小修復應該可以解決問題:

@Data
@AllArgsConstructor
public static class Department {
    private int id;
    @EqualsAndHashCode.Exclude
    private String name;
    @EqualsAndHashCode.Exclude
    private final Map<String, Integer> groupCounts = new HashMap<>();
}

但是,最好使用帶有merge函數和Supplier<Map> Collectors.toMap來實現類似的結果,而不使用Department作為映射鍵:

List<Department> result = new ArrayList<>(
    summaries
        .stream() // Stream<DepartmentSummaryRow>
        .collect(Collectors.toMap(
                DepartmentSummaryRow::getId, // int id as key
                SOGroup::create,             // value: Department
                SOGroup::merge,              // merge departments by id
                LinkedHashMap::new           // keep insertion order
        ))
        .values()
);

result.forEach(System.out::println);

需要實現幾個實用方法:

static Department create(DepartmentSummaryRow row) {
    Department dept = new Department(row.getId(), row.getName());
    dept.getGroupCounts().put(row.getGroupName(), row.getGroupMembersCount());
    return dept;
}

static Department merge(Department dept1, Department dept2) {
    dept2.getGroupCounts().forEach(
        (k, v) -> dept1.getGroupCounts().merge(k, v, Integer::sum)
    );
    return dept1;
}

輸出:

[
    {"id":1,"name":"name_a1","groupCounts":{"g1":3,"g2":9}},
    {"id":2,"name":"name_a2","groupCounts":{"g1":3,"g2":9}},
...
    {"id":49,"name":"name_a49","groupCounts":{"g1":3,"g2":9}},
    {"id":50,"name":"name_a50","groupCounts":{"g1":3,"g2":9}}
]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM