美文网首页
使用java8 stream实现列表两个字段的排序

使用java8 stream实现列表两个字段的排序

作者: GuangHui | 来源:发表于2022-09-02 09:25 被阅读0次

    最近在实现一个司机pk决策的需求,其中要对一个list对象中的两个字段排序,最后通过排序决策出pk结果。在这里,我把自己的开发实现,做一个小总结。

    场景:司机抢单pk场景,pk的因素包括两个,第一是综合得分score,第二是司机当前位置距离收货点的eta距离distance。当分数最大时,直接取综合得分最高的,当分数存在相同的时,取相同分数距离最小的那个。

    下面直接上代码,我首先为每个司机的pk过程定义了一个业务类。

    @Builder
    @Data
    public class GrabPkScoreBo {
    
        /**
         * 业务类型
         */
        private Integer businessType;
    
        /**
         * 业务订单编号
         */
        private Long businessOrderNo;
    
        /**
         * 司机id
         */
        private Long driverId;
    
        /**
         * 司机分层id
         */
        private Long layerId;
    
        /**
         * eda距离
         */
        private Long edaDistance;
    
        /**
         * 得分
         */
        private Long score;
    
        /**
         * 规则集合
         */
        List<RuleFactorBo> ruleFactorBoList;
    
        public Long getScore() {
            if (this.score != null) {
                return this.score;
            }
            if (CollUtil.isEmpty(this.ruleFactorBoList)) {
                return 0L;
            }
            long result = 0;
            for (RuleFactorBo ruleFactorBo : this.ruleFactorBoList) {
                if (layerFactor(ruleFactorBo) || distanceFactor(ruleFactorBo)) {
                    result = result + ruleFactorBo.getScore() * ruleFactorBo.getWeight();
                }
            }
            this.score = result;
            return result;
        }
    
        private boolean distanceFactor(RuleFactorBo ruleFactorBo) {
            if (RuleFactorTypeEnum.EDA_DISTANCE.getCode().equals(ruleFactorBo.getFactorType())) {
                SectionModel sectionModel = SectionUtil.transferSection(ruleFactorBo.getFactorValue());
                return this.edaDistance >= sectionModel.getStart() && this.edaDistance <= sectionModel.getEnd();
            }
            return false;
        }
    
        private Boolean layerFactor(RuleFactorBo ruleFactorBo) {
            return RuleFactorTypeEnum.DRIVER_LAYER.getCode().equals(ruleFactorBo.getFactorType()) && ruleFactorBo.getFactorValue().equals(this.layerId.toString());
        }
    }
    

    这里综合得分的计算结果,是根据业务定义的pk规则(具体pk规则长什么样,就在这里不展示了)确定的,目前pk规则主要是包括两个因子:司机分层和司机eta距离的距离分段区间。

    上述定义的类中,包括了规则因子要素,我通过重写get方法,在get方法被调用时进行计算结果,同时把计算结果赋值给对象的属性变量,当重复获取时,若属性值不为空,则直接返回,这样就避免了重复计算。

    下面是计算排序过程的实现,代码如下:

    Long sucDriverId = scoreBoList.stream()
    .min(Comparator.comparing(GrabPkScoreBo::getScore)
    .reversed()
    .thenComparing(GrabPkScoreBo::getEdaDistance))
    .map(GrabPkScoreBo::getDriverId).orElse(null);
    

    说实在的,我开始看这条语句的时候,其实也有点看不懂,但把它转换为sql语句之后,一下子就明白了。

    order by score desc,edaDistance asc limit 0,1
    

    我的理解,min起到一个limit 0,1的效果,而正常stream的排序都是一个升序排列,而reversed()反转方法,正是变升序为降序。这样就变成了先按照分数倒序排列,分数相同,再按照距离升序排列,最后取第一行数据,就得到了结果。

    相关文章

      网友评论

          本文标题:使用java8 stream实现列表两个字段的排序

          本文链接:https://www.haomeiwen.com/subject/eauenrtx.html