Java怎么模拟rank/over函数实现获取分组排名
背景
考试批次 | 班级 | 姓名 | 语文 |
---|---|---|---|
202302 | 三年一班 | 张小明 | 130.00 |
202302 | 三年一班 | 王二小 | 128.00 |
202302 | 三年一班 | 谢春花 | 136.00 |
202302 | 三年二班 | 冯世杰 | 129.00 |
202302 | 三年二班 | 马功成 | 130.00 |
202302 | 三年二班 | 魏翩翩 | 136.00 |
假设我们有如上数据,现在有一个需求需要统计各学生语文单科成绩在班级中的排名和全年段排名,你会如何实现?
很容易的我们想到了 rank() over() 实现
over()是分析函数,可以和 rank()、 dense_rank() 、 row_number() 配合使用。
使用语法如下:
RANK() OVER(PARTITION BY COLUMN ORDER BY COLUMN) dense_rank() OVER(PARTITION BY COLUMN ORDER BY COLUMN) ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN)
解释:partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。
rank()涵数主要用于排序,并给出序号 ,对于排序并列的数据给予相同序号,并空出并列所占的名次。
dense_rank() 功能同rank()一样,区别在于不空出并列所占的名次
row_number()涵数则是按照顺序依次使用 ,不考虑并列
rank 结果为 1,2,2,4 dense_rank 结果为 1,2,2,3 row_number 结果为 1,2,3,4
实际应用中,会存在数据从其他外部系统接入且数据量不大等多种情况,那么使用Java代码的方式实现分组排名的功能则显得更加方便。
详细设计及实现
排序定义类 OrderBy
public class OrderBy { private String orderByEL; /** * 是否升序 */ private boolean ascend; public OrderBy(){ //默认升序 this.ascend = true; } public String orderByEL(){ return this.orderByEL; } public OrderBy orderByEL(String orderByEL){ this.orderByEL = orderByEL; return this; } public OrderBy ascend(boolean ascend){ this.ascend = ascend; return this; } public boolean ascend(){ return this.ascend; } }
该类定义了如下属性:
排序的fileld
是否升序
获取排名方法
该方法定义如下:
<T> void rankOver(List<T> dataList, String[] partitionByFields, List<OrderBy> orderByList, String resultField, int rankType);
该方法提供了5个入参:
dataList 排序的数据集
partitionByFields 分组field的数组
orderByList 排序字段集合
resultField 排名结果存放的字段
rankType 排名方式
1:不考虑并列(row_number 结果为 1,2,3,4)
2:考虑并列,空出并列所占的名次(rank 结果为 1,2,2,4)
3:考虑并列,不空出并列所占的名次(dense_rank 1,2,2,3)
该方法具体实现如下
public static <T> void rankOver(List<T> dataList, String[] partitionByFields, List<OrderBy> orderByList, String resultField, int rankType) { if (CollectionUtils.isEmpty(orderByList)) { return; } //STEP_01 剔除掉不参与排名的数据 List<T> tempList = new ArrayList<>(); for (T data : dataList) { boolean part = true; for (OrderBy rptOrderBy : orderByList) { Object o1 = executeSpEL(rptOrderBy.orderByEL(), data); if (o1 == null) { //参与排序的值为null的话则不参与排名 part = false; break; } } if (part) { tempList.add(data); } } if (CollectionUtils.isEmpty(tempList)) { return; } //STEP_02 分组 Map<String, List<T>> groupMap = group(tempList, null, partitionByFields); for (List<T> groupDataList : groupMap.values()) { order(orderByList, groupDataList); if (rankType == 1) { int rank = 1; for (T temp : groupDataList) { setFieldValue(temp, resultField, rank); rank++; } } else { int prevRank = Integer.MIN_VALUE; int size = groupDataList.size(); for (int i = 0; i < size; i++) { T current = groupDataList.get(i); if (i == 0) { //第一名 setFieldValue(current, resultField, 1); prevRank = 1; } else { T prev = groupDataList.get(i - 1); boolean sameRankWithPrev = true;//并列排名 for (OrderBy rptOrderBy : orderByList) { Object o1 = executeSpEL(rptOrderBy.orderByEL(), current); Object o2 = executeSpEL(rptOrderBy.orderByEL(), prev); if (!o1.equals(o2)) { sameRankWithPrev = false; break; } } if (sameRankWithPrev) { setFieldValue(current, resultField, getFieldValue(prev, resultField)); if (rankType == 2) { ++prevRank; } } else { setFieldValue(current, resultField, ++prevRank); } } } } } }
使用案例
定义一个学生类:
public class Student { private String batch; private String banji; private String name; private Double yuwen; //extra private Integer rank1; private Integer rank2; public Student(String batch, String banji, String name, Double yuwen) { this.batch = batch; this.banji = banji; this.name = name; this.yuwen = yuwen; } }
我们写一个方法,返回如下数据:
public List<Student> getDataList() { List<Student> dataList = new ArrayList<>(); dataList.add(new Student("202302", "三年一班", "张小明", 130.0)); dataList.add(new Student("202302", "三年一班", "王二小", 128.0)); dataList.add(new Student("202302", "三年一班", "谢春花", 136.0)); dataList.add(new Student("202302", "三年二班", "冯世杰", 129.0)); dataList.add(new Student("202302", "三年二班", "马功成", 130.0)); dataList.add(new Student("202302", "三年二班", "魏翩翩", 136.0)); return dataList; }
获取学生语文成绩的班级排名和年段排名,排名采用并列并空出并列所占用名次的方式。
List<Student> dataList = getDataList(); List<OrderBy> orderByList = new ArrayList<>(); orderByList.add(new OrderBy().orderByEL("yuwen").ascend(false)); //获取全校排名 DataProcessUtil.rankOver(dataList, new String[]{"batch"}, orderByList, "rank1", 2); //获取班级排名 DataProcessUtil.rankOver(dataList, new String[]{"batch", "banji"}, orderByList, "rank2", 2); log("语文单科成绩排名情况如下:"); Map<String, List<Student>> groupMap = DataProcessUtil.group(dataList, null, new String[]{"batch"}); for (Map.Entry<String, List<Student>> entry : groupMap.entrySet()) { log("考试批次:" + entry.getKey()); for (Student s : entry.getValue()) { log(String.format("班级:%s 学生:%s 语文成绩:%s 班级排名:%s 全校排名:%s", s.getBanji(), s.getName(), s.getYuwen(), s.getRank2(), s.getRank1())); } log(""); }
结果如下:
语文单科成绩排名情况如下:
考试批次:202302
班级:三年一班 学生:张小明 语文成绩:130.0 班级排名:2 全校排名:3
班级:三年一班 学生:王二小 语文成绩:128.0 班级排名:3 全校排名:6
班级:三年一班 学生:谢春花 语文成绩:136.0 班级排名:1 全校排名:1
班级:三年二班 学生:冯世杰 语文成绩:129.0 班级排名:3 全校排名:5
班级:三年二班 学生:马功成 语文成绩:130.0 班级排名:2 全校排名:3
班级:三年二班 学生:魏翩翩 语文成绩:136.0 班级排名:1 全校排名:1
可以看到全校排名中 有两个并列第一名 两个并列第三名,且空出了并列所占用的名次2 和 名次4
以上是Java怎么模拟rank/over函数实现获取分组排名的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7

PHP和Python各有优势,选择应基于项目需求。1.PHP适合web开发,语法简单,执行效率高。2.Python适用于数据科学和机器学习,语法简洁,库丰富。

PHP适合web开发,特别是在快速开发和处理动态内容方面表现出色,但不擅长数据科学和企业级应用。与Python相比,PHP在web开发中更具优势,但在数据科学领域不如Python;与Java相比,PHP在企业级应用中表现较差,但在web开发中更灵活;与JavaScript相比,PHP在后端开发中更简洁,但在前端开发中不如JavaScript。

PHP和Python各有优势,适合不同场景。1.PHP适用于web开发,提供内置web服务器和丰富函数库。2.Python适合数据科学和机器学习,语法简洁且有强大标准库。选择时应根据项目需求决定。

胶囊是一种三维几何图形,由一个圆柱体和两端各一个半球体组成。胶囊的体积可以通过将圆柱体的体积和两端半球体的体积相加来计算。本教程将讨论如何使用不同的方法在Java中计算给定胶囊的体积。 胶囊体积公式 胶囊体积的公式如下: 胶囊体积 = 圆柱体体积 两个半球体体积 其中, r: 半球体的半径。 h: 圆柱体的高度(不包括半球体)。 例子 1 输入 半径 = 5 单位 高度 = 10 单位 输出 体积 = 1570.8 立方单位 解释 使用公式计算体积: 体积 = π × r2 × h (4

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP成为许多网站首选技术栈的原因包括其易用性、强大社区支持和广泛应用。1)易于学习和使用,适合初学者。2)拥有庞大的开发者社区,资源丰富。3)广泛应用于WordPress、Drupal等平台。4)与Web服务器紧密集成,简化开发部署。
