实体类
public class UserEntity{
/**
* 用户主键
*/
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 用户地址
*/
private String address;
//get set
}
第一种方式
Java中,List集合对象,默认有一个排序方法sort(Comparator<? super E> c),如果传递null,那是对简单类型的排序,如果是对象类型,并且是需要按照对象类型的某一个属性字段排序,就需要我们传入排序的规则。
new Comparator<T>() {
@Override
public int compare(UserEntity o1, UserEntity o2) {
return 0;
}
});
code
public static List<UserEntity> sort1(List<UserEntity> list, String order, boolean isDesc) {
AtomicInteger cmp = new AtomicInteger();
list.sort((u1, u2) -> {
switch (order){
case "id":
cmp.set(u1.getId() - u2.getId());
break;
case "username":
cmp.set(u1.getUsername().compareTo(u2.getUsername()));
break;
case "password":
cmp.set(u1.getPassword().compareTo(u2.getPassword()));
break;
case "address":
cmp.set(u1.getAddress().compareTo(u2.getAddress()));
break;
default:
cmp.set(1);
}
return isDesc ? -cmp.get() : cmp.get();
});
return list;
}
直接使用入参与使用 switch
语句将入参与case
进行比较,代码简单明了
但是扩展性不高,直接使用入参不安全,可能出现 空指针异常。
入参可能不能正确对应实体类字段,我们可以直接修改case的值,或者使用枚举进行校验,代码如下:
枚举类
public enum UserEnum {
/**
* 用户id
*/
USER_ID("user_id"),
/**
* 用户名
*/
USER_USERNAME("name"),
/**
* 用户密码
*/
USER_PASSWORD("pwd"),
/**
* 用户地址
*/
USER_ADDRESS("address")
;
/**
* 数据库字段
*/
private String column;
UserEnum(String column) {
this.column = column;
}
public String getColumn() {
return column;
}
/**
* 通过字段获得枚举对象
* @param column
* @return
*/
public static UserEnum getUserEnum(String column){
for (UserEnum userEnum : UserEnum.values()){
if (userEnum.column.equals(column)){
return userEnum;
}
}
return null;
}
}
code
public static List<UserEntity> sort3(List<UserEntity> list, String order, boolean isDesc) {
AtomicInteger cmp = new AtomicInteger();
UserEnum userEnum = UserEnum.getUserEnum(order);
list.sort((u1, u2) -> {
switch (Objects.requireNonNull(userEnum)){
case USER_ID:
cmp.set(u1.getId() - u2.getId());
break;
case USER_USERNAME:
cmp.set(u1.getUsername().compareTo(u2.getUsername()));
break;
case USER_PASSWORD:
cmp.set(u1.getPassword().compareTo(u2.getPassword()));
break;
case USER_ADDRESS:
cmp.set(u1.getAddress().compareTo(u2.getAddress()));
break;
default:
cmp.set(1);
}
return isDesc ? -cmp.get() : cmp.get();
});
return list;
}
该方法相对于第一种情况虽然安全性较高,但是扩展性不足,若实体类新增属性,则枚举类和switch中都需要添加对应语句
相对于第一种方法,不推荐使用
tips
Lambda表达式里面修改外部变量问题
Java中lambda表达式是匿名类语法上的进一步简化,它的本质其实还是调用对象的方法。lambda表达式会以内联的形式创建一个函数式接口的实例,保存在堆中,而局部变量则保存在栈中,而在Java中方法调用是值传递的(特别声明java中都是按值传递的!!!),所以在lambda表达式中对变量的操作都是基于原变量的副本,不会影响到原变量的值。那假如没有要求lambda表达式外部变量为final修饰,那么就会误以为外部变量的值能够在lambda表达式中被改变,而这实际是不可能的,所以要求外部变量为final。 我们知道局部变量随着方法的调用会被压入栈中,当方法调用结束时,出栈,这些局部变量全部死亡。而函数式接口实例对象生命周期和其他类对象是一样的,从创建一个实例对象开始,系统就会为该对象分配内存,直到没有引用变量指向分配给该对象得内存,它被GC垃圾回收,所以很可能出现的一种情况就是:方法已调用结束,局部变量已死亡,但实例对象的对象仍然活着。也就是说如果这个时候仍然有引诱变量指向lambda表达式在堆中所创建的实例,但是lambda表达式中的局部变量已经死亡,岂不是出了问题,这在java中是不允许的,在我还需要你的时候你的局部变量是不可以死亡的
在Java的经典著作《Effective Java》、《Java Concurrency in Practice》里,大神们都提到:匿名函数里的变量引用,也叫做变量引用泄露,会导致线程安全问题,因此在Java8之前,如果在匿名类内部引用函数局部变量,必须将其声明为final,即不可变对象。变量不可变(只是引用不可变,而不是真正的不可变),使用原子类
Java8这里加了一个语法糖:在lambda表达式以及匿名类内部,如果引用某局部变量,则直接将其视为final。在lambda表达式中访问外部变量时,都不允许有修改变量的倾向,即
1、变量必须是final类型的
2、如果没有定义成final,那么变量在初始化以后,不允许再有任何赋值的情况出现。(其实就是隐式final限制
第二种
使用反射
public static List<UserEntity> sort2(List<UserEntity> list, String orderBy, boolean isDesc){
AtomicInteger cmp = new AtomicInteger();
list.sort((u1, u2) -> {
try {
Field field = u1.getClass().getDeclaredField(orderBy);
String className = field.getType().getSimpleName();
String getMethodName = "get" + orderBy.substring(0, 1).toUpperCase() + orderBy.substring(1);
Method method = u1.getClass().getMethod(getMethodName);
if ("String".equals(className)){
cmp.set(method.invoke(u1).toString().compareTo(method.invoke(u2).toString()));
}else if ("int".equals(className) || "Integer".equals(className)){
cmp.set(Integer.parseInt(method.invoke(u1).toString()) - Integer.parseInt(method.invoke(u2).toString()));
}else {
// xxx Double Float ...
cmp.set(1);
}
} catch (Exception e) {
e.printStackTrace();
}
return isDesc ? cmp.get() : -cmp.get();
});
return list;
}
通过反射获取到实体类属性,通过字符串拼接获得get方法,然后使用invoke方法执行该方法,
问题:
-
反射性能低,
-
入参与实体类属性不一致则会
NoSuchFieldException
, 可以使用枚举根据入参获得实体类属性,再反射
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95915.html