mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add FuncComparator
This commit is contained in:
parent
3c8ea37ca8
commit
9a21da9c78
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# 5.7.10 (2021-08-22)
|
# 5.7.10 (2021-08-24)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【core 】 增加NamingCase类
|
* 【core 】 增加NamingCase类
|
||||||
@ -19,6 +19,7 @@
|
|||||||
* 【poi 】 Excel07SaxReader支持数字类型sheet名称、支持sheetName:名称前缀(issue#I46OMA@Gitee)
|
* 【poi 】 Excel07SaxReader支持数字类型sheet名称、支持sheetName:名称前缀(issue#I46OMA@Gitee)
|
||||||
* 【extra 】 Mail增加build方法(issue#I46LGE@Gitee)
|
* 【extra 】 Mail增加build方法(issue#I46LGE@Gitee)
|
||||||
* 【core 】 XmlUtil增加beanToXml重载,支持忽略null
|
* 【core 】 XmlUtil增加beanToXml重载,支持忽略null
|
||||||
|
* 【core 】 添加NullComparator、FuncComparator(issue#I471X7@Gitee)
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee)
|
* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee)
|
||||||
|
@ -312,6 +312,9 @@ public class ListUtil {
|
|||||||
* @see Collections#sort(List, Comparator)
|
* @see Collections#sort(List, Comparator)
|
||||||
*/
|
*/
|
||||||
public static <T> List<T> sort(List<T> list, Comparator<? super T> c) {
|
public static <T> List<T> sort(List<T> list, Comparator<? super T> c) {
|
||||||
|
if(CollUtil.isEmpty(list)){
|
||||||
|
return list;
|
||||||
|
}
|
||||||
list.sort(c);
|
list.sort(c);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ import java.util.Comparator;
|
|||||||
*
|
*
|
||||||
* @param <T> 被比较的Bean
|
* @param <T> 被比较的Bean
|
||||||
* @author jiangzeyin
|
* @author jiangzeyin
|
||||||
|
* @deprecated 此类不再需要,使用FuncComparator代替更加灵活
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class BaseFieldComparator<T> implements Comparator<T>, Serializable {
|
public abstract class BaseFieldComparator<T> implements Comparator<T>, Serializable {
|
||||||
private static final long serialVersionUID = -3482464782340308755L;
|
private static final long serialVersionUID = -3482464782340308755L;
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ public class CompareUtil {
|
|||||||
* @param <T> 被比较对象类型(必须实现Comparable接口)
|
* @param <T> 被比较对象类型(必须实现Comparable接口)
|
||||||
* @param c1 对象1,可以为{@code null}
|
* @param c1 对象1,可以为{@code null}
|
||||||
* @param c2 对象2,可以为{@code null}
|
* @param c2 对象2,可以为{@code null}
|
||||||
* @param isNullGreater 当被比较对象为null时是否排在前面,true表示null大于任何对象,false反之
|
* @param isNullGreater 当被比较对象为null时是否排在后面,true表示null大于任何对象,false反之
|
||||||
* @return 比较结果,如果c1 < c2,返回数小于0,c1==c2返回0,c1 > c2 大于0
|
* @return 比较结果,如果c1 < c2,返回数小于0,c1==c2返回0,c1 > c2 大于0
|
||||||
* @see java.util.Comparator#compare(Object, Object)
|
* @see java.util.Comparator#compare(Object, Object)
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package cn.hutool.core.comparator;
|
package cn.hutool.core.comparator;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ClassUtil;
|
import cn.hutool.core.util.ClassUtil;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -12,11 +14,9 @@ import java.lang.reflect.Field;
|
|||||||
* @param <T> 被比较的Bean
|
* @param <T> 被比较的Bean
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public class FieldComparator<T> extends BaseFieldComparator<T> {
|
public class FieldComparator<T> extends FuncComparator<T> {
|
||||||
private static final long serialVersionUID = 9157326766723846313L;
|
private static final long serialVersionUID = 9157326766723846313L;
|
||||||
|
|
||||||
private final Field field;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
@ -24,10 +24,7 @@ public class FieldComparator<T> extends BaseFieldComparator<T> {
|
|||||||
* @param fieldName 字段名
|
* @param fieldName 字段名
|
||||||
*/
|
*/
|
||||||
public FieldComparator(Class<T> beanClass, String fieldName) {
|
public FieldComparator(Class<T> beanClass, String fieldName) {
|
||||||
this.field = ClassUtil.getDeclaredField(beanClass, fieldName);
|
this(getNonNullField(beanClass, fieldName));
|
||||||
if (this.field == null) {
|
|
||||||
throw new IllegalArgumentException(StrUtil.format("Field [{}] not found in Class [{}]", fieldName, beanClass.getName()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,11 +33,33 @@ public class FieldComparator<T> extends BaseFieldComparator<T> {
|
|||||||
* @param field 字段
|
* @param field 字段
|
||||||
*/
|
*/
|
||||||
public FieldComparator(Field field) {
|
public FieldComparator(Field field) {
|
||||||
this.field = field;
|
this(true, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int compare(T o1, T o2) {
|
* 构造
|
||||||
return compareItem(o1, o2, this.field);
|
*
|
||||||
|
* @param nullGreater 是否{@code null}在后
|
||||||
|
* @param field 字段
|
||||||
|
*/
|
||||||
|
public FieldComparator(boolean nullGreater, Field field) {
|
||||||
|
super(nullGreater, (bean) ->
|
||||||
|
(Comparable<?>) ReflectUtil.getFieldValue(bean,
|
||||||
|
Assert.notNull(field, "Field must be not null!")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段,附带检查字段不存在的问题。
|
||||||
|
*
|
||||||
|
* @param beanClass Bean类
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 非null字段
|
||||||
|
*/
|
||||||
|
private static Field getNonNullField(Class<?> beanClass, String fieldName) {
|
||||||
|
final Field field = ClassUtil.getDeclaredField(beanClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException(StrUtil.format("Field [{}] not found in Class [{}]", fieldName, beanClass.getName()));
|
||||||
|
}
|
||||||
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.hutool.core.comparator;
|
package cn.hutool.core.comparator;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ClassUtil;
|
import cn.hutool.core.util.ClassUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
@ -12,11 +12,9 @@ import java.lang.reflect.Field;
|
|||||||
* @param <T> 被比较的Bean
|
* @param <T> 被比较的Bean
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public class FieldsComparator<T> extends BaseFieldComparator<T> {
|
public class FieldsComparator<T> extends NullComparator<T> {
|
||||||
private static final long serialVersionUID = 8649196282886500803L;
|
private static final long serialVersionUID = 8649196282886500803L;
|
||||||
|
|
||||||
private final Field[] fields;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
@ -24,23 +22,29 @@ public class FieldsComparator<T> extends BaseFieldComparator<T> {
|
|||||||
* @param fieldNames 多个字段名
|
* @param fieldNames 多个字段名
|
||||||
*/
|
*/
|
||||||
public FieldsComparator(Class<T> beanClass, String... fieldNames) {
|
public FieldsComparator(Class<T> beanClass, String... fieldNames) {
|
||||||
this.fields = new Field[fieldNames.length];
|
this(true, beanClass, fieldNames);
|
||||||
for (int i = 0; i < fieldNames.length; i++) {
|
|
||||||
this.fields[i] = ClassUtil.getDeclaredField(beanClass, fieldNames[i]);
|
|
||||||
if (this.fields[i] == null) {
|
|
||||||
throw new IllegalArgumentException(StrUtil.format("Field [{}] not found in Class [{}]", fieldNames[i], beanClass.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int compare(T o1, T o2) {
|
* 构造
|
||||||
for (Field field : fields) {
|
*
|
||||||
int compare = this.compareItem(o1, o2, field);
|
* @param nullGreater 是否{@code null}在后
|
||||||
if (compare != 0) {
|
* @param beanClass Bean类
|
||||||
return compare;
|
* @param fieldNames 多个字段名
|
||||||
|
*/
|
||||||
|
public FieldsComparator(boolean nullGreater, Class<T> beanClass, String... fieldNames) {
|
||||||
|
super(nullGreater, (a, b) -> {
|
||||||
|
Field field;
|
||||||
|
for (String fieldName : fieldNames) {
|
||||||
|
field = ClassUtil.getDeclaredField(beanClass, fieldName);
|
||||||
|
Assert.notNull(field, "Field [{}] not found in Class [{}]", fieldName, beanClass.getName());
|
||||||
|
final int compare = new FieldComparator<>(field).compare(a, b);
|
||||||
|
if (0 != compare) {
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return 0;
|
||||||
return 0;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package cn.hutool.core.comparator;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定函数排序器
|
||||||
|
*
|
||||||
|
* @param <T> 被比较的对象
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class FuncComparator<T> extends NullComparator<T> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Function<T, Comparable<?>> func;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param nullGreater 是否{@code null}在后
|
||||||
|
* @param func 比较项获取函数
|
||||||
|
*/
|
||||||
|
public FuncComparator(boolean nullGreater, Function<T, Comparable<?>> func) {
|
||||||
|
super(nullGreater, null);
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doCompare(T a, T b) {
|
||||||
|
Comparable<?> v1;
|
||||||
|
Comparable<?> v2;
|
||||||
|
try {
|
||||||
|
v1 = func.apply(a);
|
||||||
|
v2 = func.apply(b);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ComparatorException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return compare(a, b, v1, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象及对应比较的值的综合比较<br>
|
||||||
|
* 考虑到如果对象对应的比较值相同,如对象的字段值相同,则返回相同结果,此时在TreeMap等容器比较去重时会去重。<br>
|
||||||
|
* 因此需要比较下对象本身以避免去重
|
||||||
|
*
|
||||||
|
* @param o1 对象1
|
||||||
|
* @param o2 对象2
|
||||||
|
* @param v1 被比较的值1
|
||||||
|
* @param v2 被比较的值2
|
||||||
|
* @return 比较结果
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
private int compare(T o1, T o2, Comparable v1, Comparable v2) {
|
||||||
|
int result = ObjectUtil.compare(v1, v2);
|
||||||
|
if (0 == result) {
|
||||||
|
//避免TreeSet / TreeMap 过滤掉排序字段相同但是对象不相同的情况
|
||||||
|
result = CompareUtil.compare(o1, o2, this.nullGreater);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package cn.hutool.core.comparator;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code null}友好的比较器包装,如果nullGreater,则{@code null} > non-null,否则反之。<br>
|
||||||
|
* 如果二者皆为{@code null},则为相等,返回0。<br>
|
||||||
|
* 如果二者都非{@code null},则使用传入的比较器排序。<br>
|
||||||
|
* 传入比较器为{@code null},则看被比较的两个对象是否都实现了{@link Comparable}实现则调用{@link Comparable#compareTo(Object)}。
|
||||||
|
* 如果两者至少一个未实现,则视为所有元素相等。
|
||||||
|
*
|
||||||
|
* @param <T> 被比较的对象
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.10
|
||||||
|
*/
|
||||||
|
public class NullComparator<T> implements Comparator<T>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
protected final boolean nullGreater;
|
||||||
|
protected final Comparator<T> comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
* @param nullGreater 是否{@code null}最大,排在最后
|
||||||
|
* @param comparator 实际比较器
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public NullComparator(boolean nullGreater, Comparator<? super T> comparator) {
|
||||||
|
this.nullGreater = nullGreater;
|
||||||
|
this.comparator = (Comparator<T>) comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(T a, T b) {
|
||||||
|
if (a == b) {
|
||||||
|
return 0;
|
||||||
|
}if (a == null) {
|
||||||
|
return nullGreater ? 1 : -1;
|
||||||
|
} else if (b == null) {
|
||||||
|
return nullGreater ? -1 : 1;
|
||||||
|
} else {
|
||||||
|
return doCompare(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Comparator<T> thenComparing(Comparator<? super T> other) {
|
||||||
|
Objects.requireNonNull(other);
|
||||||
|
return new NullComparator<>(nullGreater, comparator == null ? other : comparator.thenComparing(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Comparator<T> reversed() {
|
||||||
|
return new NullComparator<>((false == nullGreater), comparator == null ? null : comparator.reversed());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不检查{@code null}的比较方法<br>
|
||||||
|
* 用户可自行重写此方法自定义比较方式
|
||||||
|
*
|
||||||
|
* @param a A值
|
||||||
|
* @param b B值
|
||||||
|
* @return 比较结果,-1:a小于b,0:相等,1:a大于b
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
protected int doCompare(T a, T b) {
|
||||||
|
if (null == comparator) {
|
||||||
|
if (a instanceof Comparable && b instanceof Comparable) {
|
||||||
|
return ((Comparable) a).compareTo(b);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return comparator.compare(a, b);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,6 @@
|
|||||||
package cn.hutool.core.comparator;
|
package cn.hutool.core.comparator;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean属性排序器<br>
|
* Bean属性排序器<br>
|
||||||
@ -14,12 +10,9 @@ import java.util.Comparator;
|
|||||||
*
|
*
|
||||||
* @param <T> 被比较的Bean
|
* @param <T> 被比较的Bean
|
||||||
*/
|
*/
|
||||||
public class PropertyComparator<T> implements Comparator<T>, Serializable {
|
public class PropertyComparator<T> extends FuncComparator<T> {
|
||||||
private static final long serialVersionUID = 9157326766723846313L;
|
private static final long serialVersionUID = 9157326766723846313L;
|
||||||
|
|
||||||
private final String property;
|
|
||||||
private final boolean isNullGreater;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
@ -36,39 +29,6 @@ public class PropertyComparator<T> implements Comparator<T>, Serializable {
|
|||||||
* @param isNullGreater null值是否排在后(从小到大排序)
|
* @param isNullGreater null值是否排在后(从小到大排序)
|
||||||
*/
|
*/
|
||||||
public PropertyComparator(String property, boolean isNullGreater) {
|
public PropertyComparator(String property, boolean isNullGreater) {
|
||||||
this.property = property;
|
super(isNullGreater, (bean)-> BeanUtil.getProperty(bean, property));
|
||||||
this.isNullGreater = isNullGreater;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(T o1, T o2) {
|
|
||||||
if (o1 == o2) {
|
|
||||||
return 0;
|
|
||||||
} else if (null == o1) {// null 排在后面
|
|
||||||
return isNullGreater ? 1 : -1;
|
|
||||||
} else if (null == o2) {
|
|
||||||
return isNullGreater ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Comparable<?> v1;
|
|
||||||
Comparable<?> v2;
|
|
||||||
try {
|
|
||||||
v1 = BeanUtil.getProperty(o1, property);
|
|
||||||
v2 = BeanUtil.getProperty(o2, property);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ComparatorException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return compare(o1, o2, v1, v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
||||||
private int compare(T o1, T o2, Comparable fieldValue1, Comparable fieldValue2) {
|
|
||||||
int result = ObjectUtil.compare(fieldValue1, fieldValue2, isNullGreater);
|
|
||||||
if(0 == result) {
|
|
||||||
//避免TreeSet / TreeMap 过滤掉排序字段相同但是对象不相同的情况
|
|
||||||
result = CompareUtil.compare(o1, o2, this.isNullGreater);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import cn.hutool.core.date.StopWatch;
|
|||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.util.PageUtil;
|
import cn.hutool.core.util.PageUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -158,4 +160,29 @@ public class ListUtilTest {
|
|||||||
Assert.assertEquals(4, of.size());
|
Assert.assertEquals(4, of.size());
|
||||||
Assert.assertEquals(1, sub.size());
|
Assert.assertEquals(1, sub.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sortByPropertyTest(){
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
class TestBean{
|
||||||
|
private int order;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<TestBean> beanList = ListUtil.toList(
|
||||||
|
new TestBean(2, "test2"),
|
||||||
|
new TestBean(1, "test1"),
|
||||||
|
new TestBean(5, "test5"),
|
||||||
|
new TestBean(4, "test4"),
|
||||||
|
new TestBean(3, "test3")
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<TestBean> order = ListUtil.sortByProperty(beanList, "order");
|
||||||
|
Assert.assertEquals("test1", order.get(0).getName());
|
||||||
|
Assert.assertEquals("test2", order.get(1).getName());
|
||||||
|
Assert.assertEquals("test3", order.get(2).getName());
|
||||||
|
Assert.assertEquals("test4", order.get(3).getName());
|
||||||
|
Assert.assertEquals("test5", order.get(4).getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user