请叫我头头哥

SpringBoot进阶教程(七十六)多维度排序查询

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

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/
关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

posted @ 2023-06-10 21:30  请叫我头头哥  阅读(558)  评论(0编辑  收藏  举报
//Setting ico for cnblogs