在项目中经常能遇到,需要对某些数据集合进行多维度排序的需求。对于集合多条件排序解决方案也有很多,今天我们就介绍一种,思路大致是设置一个分值的集合,这个分值是按照需求来设定大小的,再根据分值的大小对集合排序。

v需求背景

我们来模拟一个需求,现在需要查询一个用户列表,该列表需要实现的排序优先级如下:

  • 付费用户排在前,非付费用户排在后
  • 付费用户中,排序优先级:同城市的>同省的>等级高的>活跃用户>不活跃用户>其他用户
  • 非付费用户中,排序优先级:等级高的>同城市的>其他用户

v代码实现

创建user类
package com.user;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor; import java.util.ArrayList;
import java.util.List; /**
* @Author toutou
* @Date 2023/2/18
* @Des
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
private Integer id; /**
* 等级
*/
private int grade; /**
* 所在省份id
*/
private int provinceId; /**
* 所在城市ID
*/
private int cityId; /**
* 是否是活跃用户,true活跃,false不活跃
*/
private boolean lively; /**
* 是否开通支付,true开通,false未开通
*/
private boolean pay; public static List<User> getTestUserList() {
List<User> list = new ArrayList<>();
list.add(User.builder().id(0).grade(1).provinceId(1).cityId(22).lively(false).pay(false).build());
list.add(User.builder().id(1).grade(1).provinceId(1).cityId(100).lively(true).pay(true).build());
list.add(User.builder().id(2).grade(3).provinceId(5).cityId(100).lively(true).pay(true).build());
list.add(User.builder().id(3).grade(2).provinceId(1).cityId(98).lively(true).pay(true).build());
list.add(User.builder().id(4).grade(2).provinceId(1).cityId(100).lively(true).pay(true).build());
list.add(User.builder().id(5).grade(2).provinceId(3).cityId(100).lively(true).pay(true).build());
list.add(User.builder().id(6).grade(2).provinceId(1).cityId(101).lively(true).pay(false).build());
list.add(User.builder().id(7).grade(1).provinceId(6).cityId(100).lively(false).pay(true).build());
list.add(User.builder().id(8).grade(1).provinceId(1).cityId(98).lively(true).pay(false).build());
list.add(User.builder().id(9).grade(1).provinceId(5).cityId(100).lively(true).pay(true).build());
return list;
}
}

为了便于调试,我们在user类中创建一个测试方法getTestUserList,以此来生成测试数据。

创建排序帮助类
package com.util;

import java.math.BigDecimal;
import java.util.List; /**
* @Author toutou
* @Date 2023/2/18
* @Des
*/
public class SortHelper {
/**
* 比较两个BigDecimal集合
* @param left
* @param right
* @return leftList<rightList返回-1;leftList=rightList返回0;leftList>rightList返回1;
*/
public static int compareBigDecimalList(List<BigDecimal> left, List<BigDecimal> right) {
int length = Math.max(left.size(), right.size());
for (int i = 0; i < length; i++) {
if (left.size() < i + 1) {
return -1;
}
if (right.size() < i + 1) {
return 1;
} int value = left.get(i).compareTo(right.get(i));
if (value != 0) {
return value;
}
}
return 0;
}
}
排序实现类
package com.util;

import com.user.User;
import org.apache.commons.lang3.tuple.ImmutablePair; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors; import reactor.core.publisher.Flux; /**
* @Author toutou
* @Date 2023/2/18
* @Des
*/
public class Sort {
/**
* 当前用户所在城市id,所在省份id
* @param cityId
* @param provinceId
* @return
*/
public List<User> sortUserList(int cityId, int provinceId) {
// 获取初始化测试list数据
List<User> list = User.getTestUserList();
if(list == null || list.size() == 0){
return list;
} List<ImmutablePair<User, List<BigDecimal>>> userAndScore = new ArrayList<>();
for (User user : list){
// 初始化一个排序的分值list
List<BigDecimal> scoreList = Flux.range(0, 6).map(p -> BigDecimal.ZERO).collectList().block();
userAndScore.add(new ImmutablePair<>(user, scoreList));
if(user.isPay()){
// 付费用户排序,付费用户为1,非付费用户为2
scoreList.set(0, BigDecimal.valueOf(1)); if(user.getCityId() == cityId){
scoreList.set(1, BigDecimal.valueOf(1));
}else{
scoreList.set(1, BigDecimal.valueOf(2));
} if(user.getProvinceId() == provinceId){
scoreList.set(2, BigDecimal.valueOf(1));
}else{
scoreList.set(2, BigDecimal.valueOf(2));
} scoreList.set(3, BigDecimal.valueOf(-user.getGrade()));
if(user.isLively()){
scoreList.set(4, BigDecimal.valueOf(1));
}else{
scoreList.set(4, BigDecimal.valueOf(2));
} scoreList.set(5, BigDecimal.valueOf(-user.getId()));
}else{
scoreList.set(0, BigDecimal.valueOf(2));
scoreList.set(1, BigDecimal.valueOf(-user.getGrade()));
if(user.getCityId() == cityId){
scoreList.set(2, BigDecimal.valueOf(1));
}else{
scoreList.set(2, BigDecimal.valueOf(2));
} scoreList.set(3, BigDecimal.valueOf(-user.getId()));
}
} return userAndScore.stream().sorted(Comparator.comparing(p -> p.getValue(), SortHelper::compareBigDecimalList)).map(ImmutablePair::getLeft).collect(Collectors.toList());
}
}
debug效果

v源码地址

https://github.com/toutouge/javademosecond/tree/master/hellolearn

作  者:请叫我头头哥


出  处:http://www.cnblogs.com/toutou/


关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!


版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。


特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信


声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

#comment_body_3242240 { display: none }

SpringBoot进阶教程(七十六)多维度排序查询的更多相关文章

  1. SpringBoot进阶教程(七十四)整合ELK

    在上一篇文章<SpringBoot进阶教程(七十三)整合elasticsearch >,已经详细介绍了关于elasticsearch的安装与使用,现在主要来看看关于ELK的定义.安装及使用 ...

  2. SpringBoot进阶教程(七十)SkyWalking

    流行的APM(Application Performance Management工具有很多,比如Cat.Zipkin.Pinpoint.SkyWalking.优秀的监控工具还有很多,其它比如还有za ...

  3. SpringBoot进阶教程(二十六)整合Redis之共享Session

    集群现在越来越常见,当我们项目搭建了集群,就会产生session共享问题.因为session是保存在服务器上面的.那么解决这一问题,大致有三个方案,1.通过nginx的负载均衡其中一种ip绑定来实现( ...

  4. SpringBoot进阶教程(七十五)数据脱敏

    无论对于什么业务来说,用户数据信息的安全性无疑都是非常重要的.尤其是在数字经济大火背景下,数据的安全性就显得更加重要.数据脱敏可以分为两个部分,一个是DB层面,防止DB数据泄露,暴露用户信息:一个是接 ...

  5. SpringBoot进阶教程(二十九)整合Redis 发布订阅

    SUBSCRIBE, UNSUBSCRIBE 和 PUBLISH 实现了 发布/订阅消息范例,发送者 (publishers) 不用编程就可以向特定的接受者发送消息 (subscribers). Ra ...

  6. SpringBoot进阶教程(五十九)整合Codis

    上一篇博文<详解Codis安装与部署>中,详细介绍了codis的安装与部署,这篇文章主要介绍介绍springboot整合codis.如果之前看过<SpringBoot进阶教程(五十二 ...

  7. SpringBoot进阶教程(七十一)详解Prometheus+Grafana

    随着容器技术的迅速发展,Kubernetes已然成为大家追捧的容器集群管理系统.Prometheus作为生态圈Cloud Native Computing Foundation(简称:CNCF)中的重 ...

  8. SpringBoot非官方教程 | 第二十六篇: sprinboot整合elk,搭建实时日志平台

    转载请标明出处: 原文首发于https://www.fangzhipeng.com/springboot/2017/07/11/sprinboot25-elk/ 本文出自方志朋的博客 这篇文章主要介绍 ...

  9. SpringBoot进阶教程(二十八)整合Redis事物

    Redis默认情况下,事务支持被禁用,必须通过设置setEnableTransactionSupport(true)为使用中的每个redistplate显式启用.这样做会强制将当前重新连接绑定到触发m ...

  10. SpringBoot进阶教程(二十五)整合Redis之@Cacheable、@CachePut、@CacheEvict的应用

    在上一篇文章(<SpringBoot(二十四)整合Redis>)中,已经实现了Spring Boot对Redis的整合,既然已经讲到Cache了,今天就介绍介绍缓存注解.各家互联网产品现在 ...

随机推荐

  1. flutter系列之:在flutter中使用媒体播放器

    目录 简介 使用前的准备工作 在flutter中使用video_player 总结 简介 现在的app功能越来越强大,除了基本的图文之外,还需要各种各样的其他的功能,比如视频,和直播. 直播可能会比较 ...

  2. Flask框架使用蓝图划分目录、g对象、使用数据库连接池链接数据库

    目录 一.使用蓝图划分项目目录 1.蓝图的介绍 2.蓝图的使用 二.介绍flask框架的g对象 1. g对象是什么? 2. 使用代码再次解释g对象 三.使用数据库连接池链接数据库 1. 引入数据库连接 ...

  3. [Linux/CentOS]通过yum获取rpm安装包

    1 yum获取rpm安装包 有时候你需要一个软件包在离线linux系统上安装,如果自己找软件包麻烦,可以linux yum下载需要的软件包. 准备工作是找一台能够联网的linux,并准备好了yum及y ...

  4. Go 语言切片是如何扩容的?

    原文链接: Go 语言切片是如何扩容的? 在 Go 语言中,有一个很常用的数据结构,那就是切片(Slice). 切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装.它非常灵活,支 ...

  5. .NET周报 【4月第2期 2023-04-08】

    国内文章 LRU缓存替换策略及C#实现 https://www.cnblogs.com/eventhorizon/p/17290125.html 这篇文章讲述了缓存替换策略,特别是LRU算法.LRU算 ...

  6. Semantic Kernel 入门系列:🥑突破提示词的限制

    无尽的上下文 LLM的语言理解和掌握能力在知识内容的解读和总结方面提供了强大的能力. 但是由于训练数据本身来自于公共领域,也就注定了无法在一些小众或者私有的领域能够足够的好的应答. 因此如何给LLM ...

  7. MySQL(四)用户与权限管理

    用户与权限管理 用户管理 MySQL用户分为普通用户和root用户,提供了许多语句来管理包括登录.退出MySQL服务器.创建用户.删除用户.密码管理和权限管理等内容. 登录MySQL服务器 mysql ...

  8. 关于Java中代码的执行顺序

    结论 注意 只有显式的加载类 JVM才会加载到内存中 先加载父类的静态代码块 然后执行子类静态代码块 当前类存在类静态变量注意引用类型没进行赋值操作初始化为null 并不会显式的加载类又存在静态代码块 ...

  9. MySQL-带你上官网看索引

    在我之前的一篇文章中,有引用一个讨论用Hash还是Tree的问题,DB中关于查找类数据结构,除了树,还有Hash(HashMap,HashSet). 存储数据结构之争 B+树主要是照顾磁盘IO这种特殊 ...

  10. pytest的几种执行方式

    1 pytest xxxx 2 python -m pytest xxxx python -m pytest --html=./report/rep2.html test_env_pytest_ini ...