根据传入的属性进行排序

导读:本篇文章讲解 根据传入的属性进行排序,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

实体类

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方法执行该方法,

问题:

  1. 反射性能低,

  2. 入参与实体类属性不一致则会 NoSuchFieldException , 可以使用枚举根据入参获得实体类属性,再反射

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95915.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!