List的多维度排序案例演示~
文章也已同步到我的csdn:http://blog.csdn.net/u012881584/article/details/72377510
关于List的多维度排序。
日常工作中有很多关于list的排序功能, 看到公司封装的一个比较好的工具类, 这里拿来记录学习下。
public class SortBuilder<T> {
// Never make these public
static final int LEFT_IS_GREATER = 1;
static final int RIGHT_IS_GREATER = -1;
private static final Logger log = LoggerFactory.getLogger(SortBuilder.class);
private List<SortFiled<T>> sortFileds = Lists.newLinkedList();
private Map<String, Method> propertyMethodMap = null;
private boolean nullsFirst;
private boolean nullsLast;
private boolean zerosFirst;
private boolean zerosLast;
private boolean natural;
private Direction naturalDirection;
private SortBuilder() {
}
public static <C> SortBuilder<C> newBuilder(Class clazz) {
SortBuilder<C> sortBuilder = new SortBuilder<>();
PropertyDescriptor[] propertyDescriptor = BeanUtils.getPropertyDescriptors(clazz);
Map<String, Method> propertyMethodMap = new HashMap<>();
for (PropertyDescriptor pd : propertyDescriptor) {
String key = pd.getName();
Method value = pd.getReadMethod();
propertyMethodMap.put(key, value);
}
sortBuilder.propertyMethodMap = propertyMethodMap;
return sortBuilder;
}
/**
* null 排序到最前面
*/
public SortBuilder<T> nullsFirst() {
nullsFirst = true;
nullsLast = false;
return this;
}
/**
* null 排序到最后面
*/
public SortBuilder<T> nullsLast() {
nullsFirst = false;
nullsLast = true;
return this;
}
/**
* 0 排序到最后面
*/
public SortBuilder<T> zerosLast() {
zerosFirst = false;
zerosLast = true;
return this;
}
/**
* 0 排序到最前面
*/
public SortBuilder<T> zerosFirst() {
zerosFirst = true;
zerosLast = false;
return this;
}
/**
* 自然排序 {1,5,3,2,4} = {1,2,3,4,5}
*/
public SortBuilder<T> natural() {
natural = true;
return this;
}
/**
* 自然排序 {1,5,3,2,4} = {1,2,3,4,5}
*/
public SortBuilder<T> natural(Direction direction) {
natural = true;
naturalDirection = direction;
return this;
}
public SortBuilder<T> clear() {
sortFileds.clear();
return this;
}
/**
* 增加一个降序
*
* @param fieldName 属性
*/
public SortBuilder<T> addDesc(String fieldName) {
addFieldMethod(fieldName, Direction.DESC);
return this;
}
/**
* 增加一个自定义排序
*
* @param comparator 自定义比较器
*/
public SortBuilder<T> addCustom(Comparator<T> comparator) {
sortFileds.add(SortFiled.builder(Direction.CUSTOM, null, comparator));
return this;
}
/**
* 增加一个升序
*
* @param fieldName 属性
*/
public SortBuilder<T> addAsc(String fieldName) {
addFieldMethod(fieldName, Direction.ASC);
return this;
}
/**
* 增加一个字段排序模式
*
* @param fieldName 属性
* @param direction 排序方式
*/
private SortBuilder<T> addFieldMethod(String fieldName, Direction direction) {
Method method = propertyMethodMap.get(fieldName);
if (method == null) {
log.error("NoSuchMethodException field:{}", fieldName);
} else {
sortFileds.add(SortFiled.<T>builder(direction, method, null));
}
return this;
}
public SortBuilder<T> sortList(List<T> list) {
Collections.sort(list, buildComparator());
return this;
}
private Comparator<T> buildComparator() {
return new Comparator<T>() {
public int compare(T o1, T o2) {
int flag = 0;
if (sortFileds.size() == 0 && natural) {
flag = compareNatural(o1, o2, naturalDirection);
} else {
for (SortFiled<T> sortFiled : sortFileds) {
Method method = sortFiled.getMethod();
Direction direction = sortFiled.getDirection();
if (direction == Direction.CUSTOM) {
flag = sortFiled.getComparator().compare(o1, o2);
} else {
flag = this.compare(method, o1, o2, direction);
}
if (flag != 0) {
break;
}
}
}
return flag;
}
private int compare(Method method, T left, T right, Direction direction) {
int flag = 0;
try {
Object leftVal = method.invoke(left);
Object rightVal = method.invoke(right);
Class returnType = method.getReturnType();
if (nullsFirst) {
flag = compareNulls(returnType, leftVal, rightVal, false, direction);
} else if (nullsLast) {
flag = compareNulls(returnType, rightVal, leftVal, true, direction);
} else {
flag = compare(returnType, leftVal, rightVal, direction);
}
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return flag;
}
private int compareNatural(Object leftVal, Object rightVal, Direction direction) {
int flag;
if (nullsFirst) {
flag = compareNulls(leftVal, rightVal, false, direction);
} else if (nullsLast) {
flag = compareNulls(rightVal, leftVal, true, direction);
} else {
flag = compare(leftVal.getClass(), leftVal, rightVal, direction);
}
return flag;
}
private int compareNulls(Object leftVal, Object rightVal, boolean reverse, Direction direction) {
return compareNulls(leftVal == null ? null : leftVal.getClass(), leftVal, rightVal, reverse, direction);
}
private int compareNulls(Class returnType, Object leftVal, Object rightVal, boolean reverse,
Direction direction) {
if (leftVal == rightVal) {
return 0;
} else if (leftVal == null) {
return RIGHT_IS_GREATER;
} else if (rightVal == null) {
return LEFT_IS_GREATER;
} else {
return reverse ? compare(returnType, rightVal, leftVal, direction) : compare(returnType, leftVal,
rightVal, direction);
}
}
private int compareZeros(BigDecimal leftNumber, BigDecimal rightNumber, boolean reverse, Direction direction) {
if (leftNumber.compareTo(rightNumber) == 0) {
return 0;
} else if (leftNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
return RIGHT_IS_GREATER;
} else if (rightNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
return LEFT_IS_GREATER;
} else {
return reverse ? compareNumber(rightNumber, leftNumber, direction) : compareNumber(leftNumber,
rightNumber, direction);
}
}
private int compareNumber(BigDecimal leftNumber, BigDecimal rightNumber, Direction direction) {
if (leftNumber.compareTo(rightNumber) == 0) {
return 0;
} else {
return direction == null || direction == Direction.ASC ? leftNumber.compareTo(rightNumber)
: rightNumber.compareTo(leftNumber);
}
}
private int compare(Class returnType, Object leftVal, Object rightVal, Direction direction) {
int flag;
if (Number.class.isAssignableFrom(returnType) || returnType == int.class || returnType == long.class
|| returnType == double.class || returnType == float.class) {
BigDecimal leftNumber = BigDecimal.valueOf(Double.valueOf(leftVal.toString()));
BigDecimal rightNumber = BigDecimal.valueOf(Double.valueOf(rightVal.toString()));
if (zerosFirst) {
flag = compareZeros(leftNumber, rightNumber, false, direction);
} else if (zerosLast) {
flag = compareZeros(rightNumber, leftNumber, true, direction);
} else {
flag = compareNumber(leftNumber, rightNumber, direction);
}
} else {
String methodReturn1 = leftVal.toString();
String methodReturn2 = rightVal.toString();
if (direction == null || direction == Direction.ASC) {
flag = methodReturn1.compareTo(methodReturn2);
} else {
flag = methodReturn2.compareTo(methodReturn1);
}
}
return flag;
}
};
}
/**
* 排序方式:
* <p/>
* ASC:升序,DESC:降序
*/
public enum Direction {
ASC, DESC, CUSTOM
}
static class SortFiled<T> {
private Method method;
private Direction direction;
private Comparator<T> comparator;
SortFiled(Direction direction, Method method, Comparator<T> comparator) {
this.method = method;
this.direction = direction;
this.comparator = comparator;
}
@SuppressWarnings("unchecked")
public static <T> SortFiled<T> builder(Direction direction, Method method, Comparator<T> comparator) {
return new SortFiled(direction, method, comparator);
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public Comparator<T> getComparator() {
return comparator;
}
public void setComparator(Comparator<T> comparator) {
this.comparator = comparator;
}
}
}
使用方法:
@Autowired
private SeriesDiagramEntityService seriesDiagramEntityService;
@org.junit.Test
public void testSortBuilder() throws Exception{
List<SeriesDiagramEntity> list = seriesDiagramEntityService.findAll(SeriesDiagramEntity.Fields.status.eq(CommonConstants.DataStatus.PUBLISH).limit(1000));
SortBuilder.<SeriesDiagramEntity>newBuilder(SeriesDiagramEntity.class).addAsc("source").addAsc("displayOrder").addDesc("status").sortList(list);
}
使用起来很简单,我们这里写了个unit test来演示。 原理就是直接使用JavaBean中的属性进行asc或者desc的排序展示。
下面来看看它是具体怎么运行的:
- 首先获取到实体类的属性, 然后添加到propertyMethodMap中。
PropertyDescriptor[] propertyDescriptor = BeanUtils.getPropertyDescriptors(clazz);
- 然后是将用户自定义的排序字段加入到sortFileds中去:
/**
* 增加一个升序
*
* @param fieldName 属性
*/
public SortBuilder<T> addAsc(String fieldName) {
addFieldMethod(fieldName, Direction.ASC);
return this;
}
/**
* 增加一个字段排序模式
*
* @param fieldName 属性
* @param direction 排序方式
*/
private SortBuilder<T> addFieldMethod(String fieldName, Direction direction) {
Method method = propertyMethodMap.get(fieldName);
if (method == null) {
log.error("NoSuchMethodException field:{}", fieldName);
} else {
sortFileds.add(SortFiled.<T>builder(direction, method, null));
}
return this;
}
- 进行排序
public SortBuilder<T> sortList(List<T> list) {
Collections.sort(list, buildComparator());
return this;
}
private Comparator<T> buildComparator() {
return new Comparator<T>() {
public int compare(T o1, T o2) {
int flag = 0;
if (sortFileds.size() == 0 && natural) {
flag = compareNatural(o1, o2, naturalDirection);
} else {
for (SortFiled<T> sortFiled : sortFileds) {
Method method = sortFiled.getMethod();
Direction direction = sortFiled.getDirection();
if (direction == Direction.CUSTOM) {
flag = sortFiled.getComparator().compare(o1, o2);
} else {
flag = this.compare(method, o1, o2, direction);
}
if (flag != 0) {
break;
}
}
}
return flag;
}
private int compare(Method method, T left, T right, Direction direction) {
int flag = 0;
try {
Object leftVal = method.invoke(left);
Object rightVal = method.invoke(right);
Class returnType = method.getReturnType();
if (nullsFirst) {
flag = compareNulls(returnType, leftVal, rightVal, false, direction);
} else if (nullsLast) {
flag = compareNulls(returnType, rightVal, leftVal, true, direction);
} else {
flag = compare(returnType, leftVal, rightVal, direction);
}
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return flag;
}
private int compareNatural(Object leftVal, Object rightVal, Direction direction) {
int flag;
if (nullsFirst) {
flag = compareNulls(leftVal, rightVal, false, direction);
} else if (nullsLast) {
flag = compareNulls(rightVal, leftVal, true, direction);
} else {
flag = compare(leftVal.getClass(), leftVal, rightVal, direction);
}
return flag;
}
private int compareNulls(Object leftVal, Object rightVal, boolean reverse, Direction direction) {
return compareNulls(leftVal == null ? null : leftVal.getClass(), leftVal, rightVal, reverse, direction);
}
private int compareNulls(Class returnType, Object leftVal, Object rightVal, boolean reverse,
Direction direction) {
if (leftVal == rightVal) {
return 0;
} else if (leftVal == null) {
return RIGHT_IS_GREATER;
} else if (rightVal == null) {
return LEFT_IS_GREATER;
} else {
return reverse ? compare(returnType, rightVal, leftVal, direction) : compare(returnType, leftVal,
rightVal, direction);
}
}
private int compareZeros(BigDecimal leftNumber, BigDecimal rightNumber, boolean reverse, Direction direction) {
if (leftNumber.compareTo(rightNumber) == 0) {
return 0;
} else if (leftNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
return RIGHT_IS_GREATER;
} else if (rightNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
return LEFT_IS_GREATER;
} else {
return reverse ? compareNumber(rightNumber, leftNumber, direction) : compareNumber(leftNumber,
rightNumber, direction);
}
}
private int compareNumber(BigDecimal leftNumber, BigDecimal rightNumber, Direction direction) {
if (leftNumber.compareTo(rightNumber) == 0) {
return 0;
} else {
return direction == null || direction == Direction.ASC ? leftNumber.compareTo(rightNumber)
: rightNumber.compareTo(leftNumber);
}
}
private int compare(Class returnType, Object leftVal, Object rightVal, Direction direction) {
int flag;
if (Number.class.isAssignableFrom(returnType) || returnType == int.class || returnType == long.class
|| returnType == double.class || returnType == float.class) {
BigDecimal leftNumber = BigDecimal.valueOf(Double.valueOf(leftVal.toString()));
BigDecimal rightNumber = BigDecimal.valueOf(Double.valueOf(rightVal.toString()));
if (zerosFirst) {
flag = compareZeros(leftNumber, rightNumber, false, direction);
} else if (zerosLast) {
flag = compareZeros(rightNumber, leftNumber, true, direction);
} else {
flag = compareNumber(leftNumber, rightNumber, direction);
}
} else {
String methodReturn1 = leftVal.toString();
String methodReturn2 = rightVal.toString();
if (direction == null || direction == Direction.ASC) {
flag = methodReturn1.compareTo(methodReturn2);
} else {
flag = methodReturn2.compareTo(methodReturn1);
}
}
return flag;
}
};
}
这里排序规则比较多, 大概就是这样。