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