业务背景
假设当前有一个对学生进行各方面评价的对象,评价包括考察模块、考察点、评价备注。字段关系为,同一模块包含多个考察点,每个考察点有一个评价备注。
对象代码如下
@Data
@AllArgsConstructor
class Evaluation {
private String inspectionModule;
private String inspectionPoint;
private String remark;
}
接下来我们做一些测试数据,看起来会直观一些
public static void main ( String [ ] args) {
List < Evaluation > list = new ArrayList < > ( ) { {
add ( new Evaluation ( "课上表现" , "专注力" , "上课的时候很专注" ) ) ;
add ( new Evaluation ( "课上表现" , "表达能力" , "表达能力还可以" ) ) ;
add ( new Evaluation ( "逻辑思维" , "清晰度" , "思路比较清晰" ) ) ;
add ( new Evaluation ( "逻辑思维" , "敏捷程度" , "反应有点迟钝" ) ) ;
add ( new Evaluation ( "课后作业" , "卷面分" , "卷面干净整节" ) ) ;
add ( new Evaluation ( "课后作业" , "正确率" , "大约90%的样子" ) ) ;
} } ;
list. forEach ( System . out:: println ) ;
}
这里我们把创建的测试对象集合打印出来(控制台打印信息如下)
看图我们可以更加直观地看到我们数据是1个模块2个考察点这样的关系,这个时候,我们想要按照考察模块对数据进行分组。很简单,使用stream中的groupingBy即可。
技术实现
我们直接上代码
public static void main ( String [ ] args) {
List < Evaluation > list = new ArrayList < > ( ) { {
add ( new Evaluation ( "课上表现" , "专注力" , "上课的时候很专注" ) ) ;
add ( new Evaluation ( "课上表现" , "表达能力" , "表达能力还可以" ) ) ;
add ( new Evaluation ( "逻辑思维" , "清晰度" , "思路比较清晰" ) ) ;
add ( new Evaluation ( "逻辑思维" , "敏捷程度" , "反应有点迟钝" ) ) ;
add ( new Evaluation ( "课后作业" , "卷面分" , "卷面干净整节" ) ) ;
add ( new Evaluation ( "课后作业" , "正确率" , "大约90%的样子" ) ) ;
} } ;
list. forEach ( System . out:: println ) ;
Map < String , List < Evaluation > > map = list. stream ( ) . collect ( Collectors . groupingBy ( Evaluation :: getInspectionModule ) ) ;
map. forEach ( ( s, evaluations) -> System . out. println ( s + ": " + evaluations) ) ;
}
运行结果是怎样的呢?(控制台打印信息如下)
数据确实根据考察模块进行了聚合,但是它的顺序却不是按照我们事先安排好的顺序,那么原因是什么呢?我们在数据聚合成map之后,打印一下这个map的信息即可知道原因。
System . out. println ( map. getClass ( ) . getSimpleName ( ) ) ;
打印出来的信息是“HashMap”,原因找到了,因为聚合后的数据是用HashMap来收集的,所以打乱了原本的顺序。
查看源码我们也能看出来,groupingBy默认使用HashMap来收集数据。因此我们只需要把HashMap换成LinkedHashMap即可解决这个问题。
通过阅读源码我们也能看到,groupingBy本身也是支持我们去自定义一个Map实例去收集数据的。
代码优化
public static void main ( String [ ] args) {
List < Evaluation > list = new ArrayList < > ( ) { {
add ( new Evaluation ( "课上表现" , "专注力" , "上课的时候很专注" ) ) ;
add ( new Evaluation ( "课上表现" , "表达能力" , "表达能力还可以" ) ) ;
add ( new Evaluation ( "逻辑思维" , "清晰度" , "思路比较清晰" ) ) ;
add ( new Evaluation ( "逻辑思维" , "敏捷程度" , "反应有点迟钝" ) ) ;
add ( new Evaluation ( "课后作业" , "卷面分" , "卷面干净整节" ) ) ;
add ( new Evaluation ( "课后作业" , "正确率" , "大约90%的样子" ) ) ;
} } ;
list. forEach ( System . out:: println ) ;
Map < String , List < Evaluation > > map = list. stream ( ) . collect ( Collectors . groupingBy ( Evaluation :: getInspectionModule , LinkedHashMap :: new , Collectors . toList ( ) ) ) ;
map. forEach ( ( s, evaluations) -> System . out. println ( s + ": " + evaluations) ) ;
}
运行结果如下
Finish
题外话
优化聚合后的数据,增强数据可读性
public static void main ( String [ ] args) {
List < Evaluation > list = new ArrayList < > ( ) { {
add ( new Evaluation ( "课上表现" , "专注力" , "上课的时候很专注" ) ) ;
add ( new Evaluation ( "课上表现" , "表达能力" , "表达能力还可以" ) ) ;
add ( new Evaluation ( "逻辑思维" , "清晰度" , "思路比较清晰" ) ) ;
add ( new Evaluation ( "逻辑思维" , "敏捷程度" , "反应有点迟钝" ) ) ;
add ( new Evaluation ( "课后作业" , "卷面分" , "卷面干净整节" ) ) ;
add ( new Evaluation ( "课后作业" , "正确率" , "大约90%的样子" ) ) ;
} } ;
list. forEach ( System . out:: println ) ;
Map < String , List < Evaluation > > map = list. stream ( ) . collect ( Collectors . groupingBy ( Evaluation :: getInspectionModule , LinkedHashMap :: new , Collectors . toList ( ) ) ) ;
System . out. println ( map. getClass ( ) . getSimpleName ( ) ) ;
map. forEach ( ( s, evaluations) -> System . out. println ( s + ": " + evaluations) ) ;
StringBuilder msg = new StringBuilder ( ) ;
map. forEach ( ( s, evaluations) -> {
msg. append ( s) . append ( ":\r\n" ) ;
evaluations. forEach ( inspectDetail -> msg. append ( inspectDetail. getInspectionPoint ( ) ) . append ( ": " )
. append ( inspectDetail. getRemark ( ) ) . append ( "\r\n" ) ) ;
} ) ;
System . out. println ( msg) ;
}
运行结果如下