I want to construct a chapter directory. My data structure is as follow.
private Map<String, ChapterPage> chapterPageRel;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class ChapterPage {
@ApiModelProperty(value = "page list")
private List<Integer> page;
@ApiModelProperty(value = "page image list")
private List<String> chapterImageId;
}
My database data is as follow.
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_electronic_material_resource_rel")
public class ElectronicMaterialResourceRelEntity {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField(value = "material_id")
private Long materialId;
@TableField(value = "chapter_image_id")
private String chapterImageId;
@TableField(value = "chapter_name")
private String chapterName;
@TableField(value = "page")
private Integer page;
}
I want to get each chapterName
of the corresponding page Numbers and pictures, in fact, I get a strange map, as shown below.
List<ElectronicMaterialResourceRelEntity> materialResourceRelEntities = xxxx;
Map<String, Map<Integer, String>> collect =
materialResourceRelEntities
.stream()
.collect(Collectors.groupingBy(ElectronicMaterialResourceRelEntity::getChapterName, Collectors.toMap(ElectronicMaterialResourceRelEntity::getSort, ElectronicMaterialResourceRelEntity::chapterImageId)));
But I want to ask, how to collect for a class like Map<String,ChapterPage>
.
A loop is probably better here, unless you need to collect to such ChapterPage
objects in multiple places in your code or your input is already a stream and you want to avoid materializing it into a list first. Just to demonstrate how this could still be done, this is an example using a custom downstream Collector
:
Map<String, ChapterPage> res = input.stream()
.collect(Collectors.groupingBy(ElectronicMaterialResourceRelEntity::chapterName,
Collector.of(
ChapterPage::new,
(c, e) -> {
c.getPage().add(e.page());
c.getChapterImage().add(e.chapterImageId());
},
(c1, c2) -> {
c1.getPage().addAll(c2.getPage());
c2.getChapterImage().addAll(c2.getChapterImage());
return c1;
}
)));
Getters and constructors differ from your code, I used a record for ElectronicMaterialResourceRelEntity
and a POJO without annotation magic for ChapterPage
in my test.
By "loop", I don't necessarily mean a traditional loop, but a somewhat less functional approach directly using a mutable HashMap
:
Map<String, ChapterPage> res = new HashMap<>();
input.forEach(e -> {
ChapterPage c = res.computeIfAbsent(e.chapterName(), k -> new ChapterPage());
c.getPage().add(e.page());
c.getChapterImage().add(e.chapterImageId());
});
produces the same result in a less verbose way.