add FuncComparator

This commit is contained in:
Looly 2021-08-24 23:13:10 +08:00
parent 3c8ea37ca8
commit 9a21da9c78
10 changed files with 231 additions and 74 deletions

View File

@ -3,7 +3,7 @@
-------------------------------------------------------------------------------------------------------------
# 5.7.10 (2021-08-22)
# 5.7.10 (2021-08-24)
### 🐣新特性
* 【core 】 增加NamingCase类
@ -19,6 +19,7 @@
* 【poi 】 Excel07SaxReader支持数字类型sheet名称、支持sheetName:名称前缀issue#I46OMA@Gitee
* 【extra 】 Mail增加build方法issue#I46LGE@Gitee
* 【core 】 XmlUtil增加beanToXml重载支持忽略null
* 【core 】 添加NullComparator、FuncComparatorissue#I471X7@Gitee
### 🐞Bug修复
* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题issue#I46AQJ@Gitee

View File

@ -312,6 +312,9 @@ public class ListUtil {
* @see Collections#sort(List, Comparator)
*/
public static <T> List<T> sort(List<T> list, Comparator<? super T> c) {
if(CollUtil.isEmpty(list)){
return list;
}
list.sort(c);
return list;
}

View File

@ -13,7 +13,9 @@ import java.util.Comparator;
*
* @param <T> 被比较的Bean
* @author jiangzeyin
* @deprecated 此类不再需要使用FuncComparator代替更加灵活
*/
@Deprecated
public abstract class BaseFieldComparator<T> implements Comparator<T>, Serializable {
private static final long serialVersionUID = -3482464782340308755L;

View File

@ -53,7 +53,7 @@ public class CompareUtil {
* @param <T> 被比较对象类型必须实现Comparable接口
* @param c1 对象1可以为{@code null}
* @param c2 对象2可以为{@code null}
* @param isNullGreater 当被比较对象为null时是否排在true表示null大于任何对象false反之
* @param isNullGreater 当被比较对象为null时是否排在true表示null大于任何对象false反之
* @return 比较结果如果c1 &lt; c2返回数小于0c1==c2返回0c1 &gt; c2 大于0
* @see java.util.Comparator#compare(Object, Object)
*/

View File

@ -1,6 +1,8 @@
package cn.hutool.core.comparator;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;
@ -12,11 +14,9 @@ import java.lang.reflect.Field;
* @param <T> 被比较的Bean
* @author Looly
*/
public class FieldComparator<T> extends BaseFieldComparator<T> {
public class FieldComparator<T> extends FuncComparator<T> {
private static final long serialVersionUID = 9157326766723846313L;
private final Field field;
/**
* 构造
*
@ -24,10 +24,7 @@ public class FieldComparator<T> extends BaseFieldComparator<T> {
* @param fieldName 字段名
*/
public FieldComparator(Class<T> beanClass, String fieldName) {
this.field = ClassUtil.getDeclaredField(beanClass, fieldName);
if (this.field == null) {
throw new IllegalArgumentException(StrUtil.format("Field [{}] not found in Class [{}]", fieldName, beanClass.getName()));
}
this(getNonNullField(beanClass, fieldName));
}
/**
@ -36,11 +33,33 @@ public class FieldComparator<T> extends BaseFieldComparator<T> {
* @param 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;
}
}

View File

@ -1,7 +1,7 @@
package cn.hutool.core.comparator;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;
@ -12,11 +12,9 @@ import java.lang.reflect.Field;
* @param <T> 被比较的Bean
* @author Looly
*/
public class FieldsComparator<T> extends BaseFieldComparator<T> {
public class FieldsComparator<T> extends NullComparator<T> {
private static final long serialVersionUID = 8649196282886500803L;
private final Field[] fields;
/**
* 构造
*
@ -24,23 +22,29 @@ public class FieldsComparator<T> extends BaseFieldComparator<T> {
* @param fieldNames 多个字段名
*/
public FieldsComparator(Class<T> beanClass, String... fieldNames) {
this.fields = new Field[fieldNames.length];
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()));
}
}
this(true, beanClass, fieldNames);
}
@Override
public int compare(T o1, T o2) {
for (Field field : fields) {
int compare = this.compareItem(o1, o2, field);
if (compare != 0) {
return compare;
/**
* 构造
*
* @param nullGreater 是否{@code null}在后
* @param beanClass Bean类
* @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;
});
}
}

View File

@ -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;
}
}

View File

@ -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} &gt; 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小于b0:相等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);
}
}

View File

@ -1,10 +1,6 @@
package cn.hutool.core.comparator;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import java.io.Serializable;
import java.util.Comparator;
/**
* Bean属性排序器<br>
@ -14,12 +10,9 @@ import java.util.Comparator;
*
* @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 final String property;
private final boolean isNullGreater;
/**
* 构造
*
@ -36,39 +29,6 @@ public class PropertyComparator<T> implements Comparator<T>, Serializable {
* @param isNullGreater null值是否排在后从小到大排序
*/
public PropertyComparator(String property, boolean isNullGreater) {
this.property = 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;
super(isNullGreater, (bean)-> BeanUtil.getProperty(bean, property));
}
}

View File

@ -4,6 +4,8 @@ import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@ -158,4 +160,29 @@ public class ListUtilTest {
Assert.assertEquals(4, of.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());
}
}