0

0

Java怎么模拟rank/over函数实现获取分组排名

WBOY

WBOY

发布时间:2023-05-03 13:52:06

|

1934人浏览过

|

来源于亿速云

转载

背景

考试批次 班级 姓名 语文
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用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。

立即学习Java免费学习笔记(深入)”;

  • 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

  • 是否升序

获取排名方法

该方法定义如下:

 void rankOver(List dataList, String[] partitionByFields, List orderByList, String resultField, int rankType);

该方法提供了5个入参:

dataList 排序的数据集

火山方舟
火山方舟

火山引擎一站式大模型服务平台,已接入满血版DeepSeek

下载

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  void rankOver(List dataList, String[] partitionByFields, List orderByList, String resultField, int rankType) {
        if (CollectionUtils.isEmpty(orderByList)) {
            return;
        }
        //STEP_01 剔除掉不参与排名的数据
        List 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> groupMap = group(tempList, null, partitionByFields);
        for (List 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 getDataList() {
    List 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 dataList = getDataList();
List 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> groupMap = DataProcessUtil.group(dataList, null, new String[]{"batch"});
for (Map.Entry> 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速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

3

2026.01.12

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

97

2026.01.09

c++框架学习教程汇总
c++框架学习教程汇总

本专题整合了c++框架学习教程汇总,阅读专题下面的文章了解更多详细内容。

53

2026.01.09

学python好用的网站推荐
学python好用的网站推荐

本专题整合了python学习教程汇总,阅读专题下面的文章了解更多详细内容。

139

2026.01.09

学python网站汇总
学python网站汇总

本专题整合了学python网站汇总,阅读专题下面的文章了解更多详细内容。

12

2026.01.09

python学习网站
python学习网站

本专题整合了python学习相关推荐汇总,阅读专题下面的文章了解更多详细内容。

19

2026.01.09

俄罗斯手机浏览器地址汇总
俄罗斯手机浏览器地址汇总

汇总俄罗斯Yandex手机浏览器官方网址入口,涵盖国际版与俄语版,适配移动端访问,一键直达搜索、地图、新闻等核心服务。

84

2026.01.09

漫蛙稳定版地址大全
漫蛙稳定版地址大全

漫蛙稳定版地址大全汇总最新可用入口,包含漫蛙manwa漫画防走失官网链接,确保用户随时畅读海量正版漫画资源,建议收藏备用,避免因域名变动无法访问。

432

2026.01.09

php学习网站大全
php学习网站大全

精选多个优质PHP入门学习网站,涵盖教程、实战与文档,适合零基础到进阶开发者,助你高效掌握PHP编程。

49

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.4万人学习

C# 教程
C# 教程

共94课时 | 6.5万人学习

Java 教程
Java 教程

共578课时 | 45万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号