写在前面
本排序适用于内存排序,并且有强制的优先级;比如 有秒杀活动的 > 加入购物车 > 运营元素 > 店铺评分 > 历史购买 > 普通活动;本功能用户实际项目中搜索的展现以及推荐(当然优先级没有上面列的那么简单);该版本性能还可以继续提供,因为时间有限,所以先出一个版本
代码示例
- 排序元素准备:
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;
}
}
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, ordinal, 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 + "}";
}
}
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 OrderDataCompara
tor(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;
}
}
- 工具包
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);
}
}
- 其他类
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;
}
}
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;
}
}
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);
}
}
- 简单测试用例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;
}
}