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

2022-09-02  本文已影响0人  GuangHui

最近在实现一个司机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()反转方法,正是变升序为降序。这样就变成了先按照分数倒序排列,分数相同,再按照距离升序排列,最后取第一行数据,就得到了结果。

上一篇下一篇

猜你喜欢

热点阅读