本排序适用于内存排序,并且有强制的优先级

比如  有秒杀活动的 > 加入购物车 > 运营元素 > 店铺评分 > 历史购买 > 普通活动

本功能用户实际项目中搜索的展现以及推荐(当然优先级没有上面列的那么简单)

该版本性能还可以继续提供,因为时间有限,所以先出一个版本

排序元素准备:

bean包下:

OrderData(主要放置待排序的id和排序因子所对应的值)

package littlehow.sort.group.bean;


import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * OrderData
 *
 * @author littlehow
 * @time 2017-08-14 11:23
 */
public class OrderData<T> {
    private T id;

    /**
     *  得分
     */
    private Map<String, Comparable> orderScoreMap;

    /**
     * 属性
     */
    private Map<String, Object> attribute;

    public void add(String key, Comparable value) {
        if (orderScoreMap == null) orderScoreMap = new HashMap<>();
        orderScoreMap.put(key, value);
    }

    public Comparable get(String key) {
        if (orderScoreMap == null) return null;
        return orderScoreMap.get(key);
    }

    public Map<String, Comparable> getOrderScoreMap() {
        return orderScoreMap;
    }

    public void setAttribute(String key, Object value) {
        if (attribute == null) attribute = new HashMap<>();
        attribute.put(key, value);
    }

    public Object getAttribute(String key) {
        if (attribute == null) return null;
        return attribute.get(key);
    }

    /**
     * 兼容前期已经调用的版本
     * @param map
     */
    @Deprecated
    public void setOrderScoreMap(Map<String, Double> map) {
        Set<Map.Entry<String, Double>> entrySet = map.entrySet();
        for (Map.Entry<String, Double> entry : entrySet) {
            add(entry.getKey(), entry.getValue());
        }
    }

    public T getId() {
        return id;
    }

    public void setId(T id) {
        this.id = id;
    }

    public boolean equalsProperty(OrderData<T> other, String key) {
        if (other == null) return false;
        return this.get(key).equals(other.get(key));
    }
}

OrderDirect 主要指明排序方向

package littlehow.sort.group.bean;

/**
 * OrderDirect 排序方向
 *
 * @author littlehow
 * @time 2017-08-14 11:10
 */
public enum OrderDirect {
    DESC(-1),//倒序
    ASC(1)//正序
    ;
    public final int v;
    OrderDirect(int v) {
        this.v = v;
    }
}

OrderProperty 排序要素

package littlehow.sort.group.bean;

/**
 * OrderProperty 排序要素
 *
 * @author littlehow
 * @time 2017-08-14 11:11
 */
public class OrderProperty implements Comparable<OrderProperty> {
    /**
     *  要素标识
     */
    private String id;

    /**
     * 要素序号
     */
    private int ordinal;

    /**
     * 要素排序方向
     */
    private OrderDirect orderDirect;

    /**
     * 是否为是非因子
     */
    private boolean trueOrFalse;

    /**
     * 为null的时候的默认值
     */
    private Comparable defaultNullValue = 0.0;

    public OrderProperty() {
    }

    public OrderProperty(String id, int ordinal) {
        this.id = id;
        this.ordinal = ordinal;
        //默认倒序
        this.orderDirect = OrderDirect.DESC;
    }

    public OrderProperty(String id, Integer ordinal, OrderDirect orderDirect) {
        this.id = id;
        this.ordinal = ordinal;
        this.orderDirect = orderDirect;
    }

    public OrderProperty(String id, int ordinal, OrderDirect orderDirect, boolean trueOrFalse) {
        this(id, ordinal, orderDirect);
        this.trueOrFalse = trueOrFalse;
    }

    public OrderProperty(String id, int ordinal, OrderDirect orderDirect, boolean trueOrFalse, Comparable defaultNullValue) {
        this(id, ordinal, orderDirect, trueOrFalse);
        this.defaultNullValue = defaultNullValue;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getOrdinal() {
        return ordinal;
    }

    public void setOrdinal(int ordinal) {
        this.ordinal = ordinal;
    }

    public OrderDirect getOrderDirect() {
        return orderDirect;
    }

    public void setOrderDirect(OrderDirect orderDirect) {
        this.orderDirect = orderDirect;
    }

    public boolean isTrueOrFalse() {
        return trueOrFalse;
    }

    public void setTrueOrFalse(boolean trueOrFalse) {
        this.trueOrFalse = trueOrFalse;
    }

    public Comparable getDefaultNullValue() {
        return defaultNullValue;
    }

    public void setDefaultNullValue(Comparable defaultNullValue) {
        this.defaultNullValue = defaultNullValue;
    }

    /**
     * 正序排列 ASC
     * @param other
     * @return
     */
    public int compareTo(OrderProperty other) {
        return this.ordinal > other.ordinal ? 1 : this.ordinal == other.ordinal ? 0 : -1;
    }

    @Override
    public String toString() {
        return "{id=" + id + ", ordinal=" + ordinal + ", orderDirect=" + orderDirect + "}";
    }
}

OrderDataComparator主要用于因子值之间的排序

package littlehow.sort.group.bean;

import java.util.Comparator;
import java.util.List;

/**
 * OrderDataComparator
 *
 * @author littlehow
 * @time 2017-08-15 15:50
 */
public class OrderDataComparator implements Comparator<OrderData> {
    private List<OrderProperty> orderProperties;
    public OrderDataComparator(List<OrderProperty> orderProperties) {
        this.orderProperties = orderProperties;
    }

    /**
     * 进行排序
     * @param o1
     * @param o2
     * @return
     */
    public int compare(OrderData o1, OrderData o2) {
        for (OrderProperty orderProperty : orderProperties) {
            //排序key
            String key = orderProperty.getId();
            //排序默认值
            Comparable dv = orderProperty.getDefaultNullValue();
            //排序方向
            OrderDirect orderDirect = orderProperty.getOrderDirect();
            //元素一对应排序因子
            Comparable c1 = o1.get(key);
            if (c1 == null) c1 = dv;
            //元素二对应排序因子
            Comparable c2 = o2.get(key);
            if (c2 == null) c2 = dv;

            if (c1.compareTo(c2) == 0) {
                continue;//如果是一样的,则进行下一排序因子的比较
            }
            return c1.compareTo(c2) * orderDirect.v;
        }
        //如果抵达这一步,证明前面要素比较全部一致,不需要进行元素交换
        return 0;
    }
}

utils包下

SortUtils 主要排序工具

package littlehow.sort.group.utils;


import littlehow.sort.group.bean.OrderData;
import littlehow.sort.group.bean.OrderDataComparator;
import littlehow.sort.group.bean.OrderProperty;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * SortUtil
 *
 * @author littlehow
 * @time 2017-08-14 11:42
 */
public abstract class SortUtils {
    /**
     * 进行实际排序
     * @param orderDatas
     * @param orderProperties
     * @param max
     */
    public static <T> void order(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties, int max) {
        //将要素要优先级排序, 优先级高的排在前面
        Collections.sort(orderProperties);
        sort(orderDatas, new OrderDataComparator(orderProperties), max);
    }

    /**
     *
     * @param list
     * @param comparator
     * @param maxSize
     */
    public static <T> void sort(List<OrderData<T>> list, Comparator<OrderData> comparator, int maxSize) {
        int length = list.size();
        if (maxSize > length) maxSize = length;
        bubble(list, (Comparator) comparator, maxSize);
    }

    /**
     *  支持局部排序
     * @param targets
     * @param comparator
     * @param maxSize     -- 想要排序的数量
     */
    private static <T> void bubble(List<OrderData<T>> targets, Comparator comparator, int maxSize) {
        int length = targets.size();
        for (int i = 0; i < maxSize; i++) {
            for (int j = i + 1; j < length; j++) {
                if (comparator.compare(targets.get(i), targets.get(j)) > 0) {//进行元素置换
                    swap(targets, i, j);
                }
            }
        }
    }

    /**
     * 元素交换
     * @param targets
     * @param i
     * @param j
     * @param <T>
     */
    private static <T> void swap(List<OrderData<T>> targets, int i, int j) {
        OrderData<T> tmp = targets.get(i);
        targets.set(i, targets.get(j));
        targets.set(j, tmp);
    }
}

CollectionUtils 集合工具,可以使用其他开源的集合工具,主要是不希望依赖太多

package com.kom.base.sort.utils;


import java.lang.reflect.Field;
import java.util.*;

/**
 * CollectionUtils
 *
 * @author littlehow
 * @time 2017-06-09 10:07
 */
public class CollectionUtils {

    private static final List EMPTY_LIST = new ArrayList();
    //私有构造,也可把类提取成抽象类
    private CollectionUtils(){}

    /**
     * 判断集合是否为空
     * @param collection
     * @return
     */
    public static boolean isEmpty(Collection collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 判断集合非空
     * @param collection
     * @return
     */
    public static boolean isNotEmpty(Collection collection) {
        return !isEmpty(collection);
    }


    /**
     * 截取list
     * @param orig
     * @param startIndex
     * @param <T>
     * @return
     */
    public static <T> List<T> subList(List<T> orig, int startIndex) {
        if (isEmpty(orig) || startIndex >= orig.size()) {
            return EMPTY_LIST;
        }
        if (startIndex == 0) return orig;
        return subList(orig, startIndex, orig.size());
    }

    /**
     * 截取list集合 前闭后开区间, 如果起始位置为0,终止位置和集合总条数一致,则直接返回原有集合
     * @param orig        -- 要截取的集合
     * @param startIndex  -- 起始下标
     * @param endIndex    -- 终止下标但不包含该值
     * @param <T>
     * @return
     */
    public static <T> List<T> subList(List<T> orig, int startIndex, int endIndex) {
        if (isEmpty(orig) || startIndex >= endIndex || startIndex >= orig.size() || endIndex > orig.size()) {
            return EMPTY_LIST;
        }
        //如果起始位置为0,终止位置和集合总条数一致,则直接返回原有集合
        if (orig.size() == endIndex && startIndex == 0) return orig;
        List<T> desc = new ArrayList<T>(endIndex - startIndex);
        for (int i = startIndex; i < endIndex; i++) {
            desc.add(orig.get(i));
        }
        return desc;
    }
}

service包下

SortService排序接口,主要便于以后扩展排序实现使用

package littlehow.sort.group.service;


import littlehow.sort.group.bean.OrderData;
import littlehow.sort.group.bean.OrderProperty;

import java.util.ArrayList;
import java.util.List;

/**
 * SortService
 *
 * @author littlehow
 * @time 2017-08-16 13:58
 */
public abstract class SortService {
    /**
     * 全体排序
     * @param orderDatas
     * @param orderProperties
     * @param <T>
     * @return
     */
    public abstract  <T> List<T> sort(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties);

    /**
     * 按照top max 排序
     * @param orderDatas
     * @param orderProperties
     * @param maxSize
     * @param <T>
     * @return
     */
    public abstract  <T> List<T> sortMax(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties, int maxSize);

    /**
     * 分页排序
     * @param orderDatas
     * @param orderProperties
     * @param pageNo
     * @param pageSize
     * @param <T>
     * @return
     */
    public abstract <T> List<T> sortPaging(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties, int pageNo, int pageSize);

    /**
     * 获取id
     * @param orderDatas
     * @param start
     * @param end
     * @return
     */
    public final <T> List<T> getIds(List<OrderData<T>> orderDatas, int start, int end) {
        List<T> ids = new ArrayList<>(orderDatas.size());
        for (int i = start; i < end; i++) {
            ids.add(orderDatas.get(i).getId());
        }
        return ids;
    }
}

GroupSortService 分组排序实现类

package littlehow.sort.group.service;


import littlehow.sort.group.bean.OrderData;
import littlehow.sort.group.bean.OrderProperty;
import littlehow.sort.group.utils.CollectionUtils;
import littlehow.sort.group.utils.SortUtils;

import java.util.List;

/**
 * SortService
 *
 * @author littlehow
 * @time 2017-08-16 12:24
 */
public class GroupSortService extends SortService{
    @Override
    public <T> List<T> sort(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties) {
        if (CollectionUtils.isEmpty(orderDatas)) return null;
        return sortMax(orderDatas, orderProperties, orderDatas.size());
    }


    @Override
    public <T> List<T> sortMax(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties, int maxSize) {
        if (CollectionUtils.isEmpty(orderDatas) || CollectionUtils.isEmpty(orderProperties)) return null;
        if (orderDatas.size() == 1) {//只有一个元素就没有排序的必要
            return getIds(orderDatas, 0, 1);
        }
        if (maxSize > orderDatas.size()) {
            maxSize = orderDatas.size();
        }
        //进行排序
        SortUtils.order(orderDatas, orderProperties, maxSize);
        return getIds(orderDatas, 0, maxSize);
    }

    @Override
    public <T> List<T> sortPaging(List<OrderData<T>> orderDatas, List<OrderProperty> orderProperties, int pageNo, int pageSize) {
        if (CollectionUtils.isEmpty(orderDatas) || CollectionUtils.isEmpty(orderProperties)) return null;
        int start = (pageNo - 1) * pageSize;
        int end = start + pageSize;
        //分页已经超出范围
        if (start >= orderDatas.size()) return null;
        if (end > orderDatas.size()) {
            end = orderDatas.size();
        }
        //进行排序
        SortUtils.order(orderDatas, orderProperties, end);
        return getIds(orderDatas, start, end);
    }
}

test包下

TestSort 简单的测试用例类

package littlehow.sort.group.test;

import littlehow.sort.group.bean.OrderData;
import littlehow.sort.group.bean.OrderDirect;
import littlehow.sort.group.bean.OrderProperty;
import littlehow.sort.group.service.GroupSortService;
import littlehow.sort.group.service.SortService;

import java.util.*;

/**
 * TestSort
 *
 * @author littlehow
 * @time 2017-08-14 17:08
 */
public class TestSort {

    static double[] orderScore = {1.5, 1.8, 2.9, 3.3, 1.5, 1.6, 3.1, 2.9,0,1,2,1.6,0,1.5, 0.7, 0,7, 1.5, 1.5, 3.3, 1.6, 4.2,1,1,1.5, 4.1,1.5,1.6};
    static double[] trueFalse = {0.0, 1.0};
    static Random random = new Random();
    static int length = orderScore.length;
    static SortService sortService = new GroupSortService();

    private static List<OrderProperty> orderProperties = new ArrayList<OrderProperty>();
    static {
        orderProperties.add(new OrderProperty("seckill", 1, OrderDirect.DESC, true));
        orderProperties.add(new OrderProperty("cart", 2, OrderDirect.ASC, true));
        orderProperties.add(new OrderProperty("marketingText", 3, OrderDirect.ASC));
        orderProperties.add(new OrderProperty("shopScore", 4, OrderDirect.DESC));
        orderProperties.add(new OrderProperty("historyPurchase", 5, OrderDirect.ASC));
        orderProperties.add(new OrderProperty("activity", 6, OrderDirect.ASC));
    }
    public static void main(String[] args) {
        test();
    }

    static void test() {
        List<OrderData<Integer>> orderDatas = new ArrayList<>();
        for (int i=1; i<11; i++) {
            orderDatas.add(getOrderData(i));
        }
        for (OrderData orderData : orderDatas) {
            System.out.println(orderData.getId() + ":" + orderData.getOrderScoreMap());
        }
//        System.out.println("========================分割===============================");
        long start = System.currentTimeMillis();
        for (int i=0;i<1;i++) {
            //Collections.shuffle(orderDatas);
//            for (OrderData orderData : orderDatas) {
//                System.out.print(orderIntData.getId()+"-");
//            }            List<Integer> result = sortService.sortPaging(orderDatas, orderProperties, 1, 10);            System.out.println(result);        }        System.out.println("-------------------->耗时:" + (System.currentTimeMillis() - start) + "毫秒");    }    static OrderData getOrderData(int id) {        OrderData orderData = new OrderData();        orderData.setId(id);        orderData.setOrderScoreMap(getOrderScore());        return orderData;    }    static Map<String, Double> getOrderScore() {        Map<String, Double> map = new HashMap<String, Double>();        for (OrderProperty orderProperty : orderProperties) {            if (random.nextInt(10) < 4) continue;            if (orderProperty.isTrueOrFalse()) {                map.put(orderProperty.getId(), trueFalse[random.nextInt(2)]);            } else {                map.put(orderProperty.getId(), orderScore[random.nextInt(length)]);            }        }        return map;    }}