一、需求:
最近接到一个需求,要求对成绩单的成绩进行排序。不是按照成绩排序,而是按照课程类别和上课的时间进行排序。我首先想到的是双循环,但是我认为这样的话if-else要写到猴年马月。
之后我开始回忆以前的数据结构了,不论是二分法还是快排,他们分明比较的都是数字大小啊,字符串虽然可以比较,但是现在课程的排序是人为自定义的,我怎么比较呢?
二、问题:
根据现在遇到的情况我就认为,想要排序必然要把字符串转换成数字,毕竟排序都是数字的,那么只有这样才能够直接比较。因此很自然的需要创建一个HashMap存放课程类别和它对应的大小。
成绩单长这样。
三、业务介绍
我这里的课程类别是一个对象,因为课程类别里面有一级学科二级学科这样,不过这是业务知识不重要,因此直接开始讲我的解决方案。在实际业务当中,数据还有老成绩和新成绩的说法,所以判断的地方非常多,有一点点小复杂。
四、代码
使用的是Collections下的sort方法,传入你需要排序的List以及排序方法。
//自定义排序1
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() - o2.getId();
}
});
前面提到过,既然是自定义排序,那么一定要对排序的东西事先定义好排序大小。
//为给课程排序,自定义排序规则,key是course_category的id,value是排名
private static Map<Long,Integer> zsCourseSortMap = new HashMap<>();//专硕
private static Map<Long,Integer> xsCourseSortMap = new HashMap<>();//学硕
static {
xsCourseSortMap.put(1249L,0);//学位公共课(必修)
xsCourseSortMap.put(18L,1);//学位基础课(必修)
xsCourseSortMap.put(19L,2);//学位专业课(必修)
xsCourseSortMap.put(20L,3);//学位专业课(选修)
xsCourseSortMap.put(21L,4);//跨学科或跨专业课程(选修)
xsCourseSortMap.put(23L,5);//公共选修课
xsCourseSortMap.put(-1L,100);//其它
zsCourseSortMap.put(1254L,0);//公共课 必修
zsCourseSortMap.put(1265L,0);//公共课 通识
zsCourseSortMap.put(18L,1);//学位基础课
zsCourseSortMap.put(670L,2);//专业必修课
zsCourseSortMap.put(671L,3);//专业选修课
zsCourseSortMap.put(672L,4);//专业实践
zsCourseSortMap.put(-1L,100);//
}
解释一下,这里的Key是课程类别对象的id,value是我自定义的大小,到时候只需要判断类别的id就能判断是哪一门课类别,之所以不直接存对象是我觉得这里new对象是一件很麻烦的事情,如果还要从数据库中一个个找出来存好,或许增加了不必要的开销。使用static是因为这些资源是亘古不变的,不随对象的变化而变化。
这里介绍一下传入的成绩列表。传进去的每一个成绩实际上是一个Map而不是Score对象。因为业务上表要达到范式要求,表的属性不能要啥都有啥,很多时候是精简的,Score本身可能并不存课程类别之类的。因此实际上获取的数据是已经封装好的Map格式
List<Map<String, Object>> scoreList;
private void sortScoreListByCategory(List<Map<String,Object>> scoreList, Map<Long,Integer> degreeTypeMap) {
Collections.sort(scoreList, new Comparator<Map<String, Object>>() {
@Override
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
CourseCategory o1Category = new CourseCategory();
CourseCategory o2Category = new CourseCategory();
if( o1.get("courseCategory") instanceof CourseCategory) { //能否转型
o1Category = (CourseCategory) o1.get("courseCategory");
} else {
Map map1 =(Map) o1.get("courseCategory");
o1Category = courseCategoryRepository.findByFirstCategoryAndSecondCategory(map1.get("firstCategory").toString(), map1.get("secondCategory").toString());
}
if( o2.get("courseCategory") instanceof CourseCategory) {
o2Category = (CourseCategory) o2.get("courseCategory");
} else {
Map map2 =(Map) o2.get("courseCategory");
o2Category = courseCategoryRepository.findByFirstCategoryAndSecondCategory(map2.get("firstCategory").toString(), map2.get("secondCategory").toString());
}
String o1studyTime = (String) o1.get("studyTime");
String o2studyTime = (String) o2.get("studyTime");
if(o1Category.getId() != o2Category.getId()) { //判断课程类别ID是否相等
return degreeTypeMap.get(o1Category.getId()) == null ? degreeTypeMap.get(-1L) : degreeTypeMap.get(o1Category.getId()).compareTo(degreeTypeMap.get(o2Category.getId()) == null ? degreeTypeMap.get(-1L) : degreeTypeMap.get(o2Category.getId()));
} else if (!o1studyTime.equals(o2studyTime)){
return o1studyTime.compareTo(o2studyTime);
}
return 0;
}
});
}
五、代码解释
根据最上面的格式,sort传进去一个List和comparator。degreeTypeMap是最前面第二个自定义的Map排序规则,要用到。
compare里面其实就是传进去两个List里存放的两个随意对象。因为本质是两两比较。
严谨点需要先判断,Map里取出的courseCategory(课程类别)能不能转型成课程类别,如果不能则说明是老数据(业务问题),从数据库中找出这个课程类别对象。那么至此就获取了需要比较的这两个对象的课程类别。
核心: 关键就是下面的如果课程类别的id不相等则说明要比较,然后从自定义的Map中获取这门课程类别的排序值,但是这里需要多一个判断,因为自定义排序可能有其他的类型,如果没有找到这门课的课程类别说明是其它,按照其它处理。
获取两门课的排序值以后,直接对他门进行.compareTo就行了当然相减也可以。
如果课程类别的id一样则再判断他们课程的学年就行了,这里因为学年是时间字符串,可以直接比较。
升序降序 :
@Override
public int compareTo(Student o1, Student o2) {
//降序
//return o1.compareTo(o2);
//升序
return o2.compareTo(o1);
}
根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数
降序就是由下往上是从大到小。
暂时先这样写