zoukankan      html  css  js  c++  java
  • JavaList集合排序之Comparable与Comparator的使用

    Collections类说明

    Collections是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。其中就有用于集合排序的sort方法,此方法主要是通过ComparableComparator来实现排序。以下介绍两者之间的使用以及区别。

    Comparable用法

    注意:在使用Collections的sort(List<T> list) 方法排序时,要求集合元素(对象)T必须是Comparable接口的实现类,同时重写Comparable的抽象方法 - int compareTo(T t)。方法说明如下:

     *  在使用Collection的sort排序的集合元素都必须是Comparable接口的实现类,该接口表示子类是可以比较的。
     *  同时实现接口必须重写抽象方法。
     *  - int compareTo(T t);
     *  该方法用于使当前对象与给定对象进行比较。
     *  返回值为一个int值,该值表示大小关系,它不关注具体的取值是多少,而关注的是取值范围。
     *  当返回值>0时:当前对象比参数对象大(T是传入的参数,this表示当前对象)
     *  当返回值<0时:当前对象比参数对象小(T是传入的参数,this表示当前对象)
     *  当返回值=0时:当前对象等于参数对象(T是传入的参数,this表示当前对象)

    代码案例一(Java基本包装类型排序)

    Integer、String等一些基本包装类,JDK默认都实现了Comparable接口,所以可以使用方法一直接进行比较排序(自然排序)

    package example.demo03;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @author yuanchaoyong
     */
    public class ComparatorTest {
    
        public static void main(String[] args) {
            // 测试案例1
            testInteger();
            // 测试案例2
            testString();
        }
    
        /**
         * List<Integer> 排序测试
         *              Integer 默认实现了Comparable接口
         *              Collections.sort默认是升序排序(自然排序)
         */
        public static  void   testInteger(){
            List<Integer> integers = Arrays.asList(12,7,15,4);
            Collections.sort(integers);
            System.out.println(integers);
        }
    
        /**
         * List<String> 排序测试
         *              String 默认实现了Comparable接口
         *              Collections.sort默认是升序排序(自然排序)
         */
        public static  void   testString(){
            List<String> strings = Arrays.asList("aaa","ccc","bbb","rrr");
            Collections.sort(strings);
            System.out.println(strings);
        }
    
    }

    输出结果

     

    代码案例二 (自定义对象排序)

    平常工作开发的时候,基本包装类型一般无法满足需求。所以我们定义了自定义对象,有时候需要根据对象的某一个属性进行排序。这个时候需要对象实现Comparable接口,同时重写Comparable的抽象方法 - int compareTo(T t)。这里以用户(User)年龄进行升序排序案例说明。

    package example.demo03;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @author yuanchaoyong
     */
    public class ComparatorTest02 {
    
        public static void main(String[] args) {
            List<User> users = Arrays.asList(new User("王五",50),new User("张三",30),new User("李四",40));
            Collections.sort(users);
            System.out.println(users);
        }
    
    }
    
    /**
     * 用户对象
     *          name
     *          age
     */
    class User   implements Comparable<User>{
    
        String name;
        Integer age;
    
        User(String n, Integer a) {
            name = n;
            age = a;
        }
    
        @Override
        public int compareTo(User u) {
            return this.age - u.age ;
        }
    
        @Override
        public String toString() {
            return String.format("{name=%s, age=%d}", name, age);
        }
    
    }

     

    Comparable优缺点

    通过以上案例知道,Comparable的确实现了我们想要的排序结果。但是使用Collections的sort(List<T> list) 方法排序集合时,集合元素必须实现Comparable接口并且定义比较规则。这个时候就开始出现一些问题。比如我们同一个工程使用了User对象,单个场景使用age进行排序,但是另一种情况下使用name排序,这个时候就出现了冲突。并且集合元素需要去实现Comparable接口,这对于我们的代码会有一些"侵入性",也不利于我们后续的代码扩展。

    所以一般不建议使用,推荐使用Collections的sort(List<T> list, Comparator<? super T> c)的重载方法。

    Comparator用法

    Collections的sort(List<T> list, Comparator<? super T> c)

     * 重载的sort方法要求传入一个外部的比较器。
     * 该方法不再要求集合元素必须实现Comparable接口。
     * 并且也不再使用集合元素自身的比较规则排序了,而是根据给定的这个额外的比较器的比较规则,对集合元素进行排序。
     * 实际开发中也推荐使用这种方式排序集合元素,若集合元素是自定义的,创建比较器时也推荐使用匿名内部类的形式。

    代码案例一 (自定义对象排序)

    package example.demo03;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    /**
     * @author yuanchaoyong
     */
    public class ComparatorTest03 {
    
        public static void main(String[] args) {
            List<Student> students = Arrays.asList(new Student("张三",30),new Student("李四",40),new Student("王五",50));
            // 使用匿名内部类进行排序
            Collections.sort(students, new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    // 自定义排序规则
                    return o1.age- o2.age;
                }
            });
            System.out.println(students);
        }
    
    
    }
    
    class  Student {
    
        String name;
        Integer age;
    
        Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return String.format("{name=%s, age=%d}", name, age);
        }
    }

    结果

     

    代码案例二 (使用List自带的排序方法)

    package example.demo03;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    /**
     * @author yuanchaoyong
     */
    public class ComparatorTest03 {
    
        public static void main(String[] args) {
            //test01();
            test02();
        }
    
        /**
         * 使用List自带的排序方法
         */
        private static void test02() {
            List<Student> students = Arrays.asList(new Student("张三",30),new Student("李四",40),new Student("王五",50));
            students.sort(new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    return o1.age- o2.age;
                }
            });
            System.out.println(students);
        }
    
        /**
         * Collections.sort方法测试
         */
        private static void test01() {
            List<Student> students = Arrays.asList(new Student("张三",30),new Student("李四",40),new Student("王五",50));
            // 使用匿名内部类进行排序
            Collections.sort(students, new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    // 自定义排序规则
                    return o1.age- o2.age;
                }
            });
            System.out.println(students);
        }
    }
    
    class  Student {
        String name;
        Integer age;
        Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return String.format("{name=%s, age=%d}", name, age);
        }
    }

     

    JDK8之lambda简化写法

    Comparator接口是一个函数式接口,所以我们可以使用更优秀的写法,简化代码。

    函数式接口就是只定义一个抽象方法的接口。
    JDK1.8开始支持默认方法,即便这个接口拥有很多默认方法,只要接口只有一个抽象方法,那么这个接口就是函数式接口。
    package example.demo03;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     * @author yuanchaoyong
     */
    public class ComparatorTest04 {
    
        public static void main(String[] args) {
            test02();
        }
    
        /**
         * lambda写法测试
         */
        private static void test02() {
            List<Teacher> teachers = Arrays.asList(new Teacher("张三", 30), new Teacher("李四", 40), new Teacher("王五", 50));
            // 多种写法,以下只是作为参考
    
            // 写法1
            // teachers.stream().sorted((o1,o2)-> o1.age- o2.age).collect(Collectors.toList());
    
            // 写法2
            //Collections.sort(teachers,(o1,o2)-> o1.getAge()- o2.getAge());
    
            // 写法3
            List<Teacher> teacherList = teachers.stream().
                    sorted((Comparator.comparingInt(Teacher::getAge)))
                    .collect(Collectors.toList());
    
            System.out.println(teachers);
        }
    }
    
    class  Teacher {
        String name;
        Integer age;
        Teacher(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        @Override
        public String toString() {
            return String.format("{name=%s, age=%d}", name, age);
        }
    }

    额外参考资源

    List<类> list; 代表某集合
     
    //返回 对象集合以类属性一升序排序
    list.stream().sorted(Comparator.comparing(类::属性一));
     
    //返回 对象集合以类属性一降序排序 注意两种写法
    list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序
    list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序
     
    //返回 对象集合以类属性一升序 属性二升序
    list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));
     
    //返回 对象集合以类属性一降序 属性二升序 注意两种写法
    list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
    list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序
     
    //返回 对象集合以类属性一降序 属性二降序 注意两种写法
    list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序
    list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序
     
    //返回 对象集合以类属性一升序 属性二降序 注意两种写法
    list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序
    list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序
    
    //空/Null数据排序
    list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(item -> item.属性二, Comparator.nullsLast(Date::compareTo))).collect(Collectors.toList());
    
    //空/Null数据分组
    Map<String, List<类>> map = list.stream().collect(Collectors.groupingBy(item -> {
      if (item.属性一 == null || item.属性一.equals("")) {
        return "";
      }
      return DateFormat.getDateInstance().format(item.属性一);
    }))

     

     

    人要耐得住寂寞,才能守得住繁华。人生最痛苦的就是拿不起放不下,不属于自己的快乐,及时放手也许是一种解脱,生活中没有谁对谁错,只有适不适合。当发现很多已经改变,更要面对的是事实。
  • 相关阅读:
    PyQt(Python+Qt)学习随笔:QMdiArea多文档界面区域的viewMode、documentMode、tabsClosable、tabPosition等属性介绍
    书籍收藏
    通用经验:大企业如何逆势而上
    T 恤
    柳传志:做企业家还是政治家
    管理最大的挑战是怎么激活人,尤其是激发年轻人
    张瑞敏:企业要长“第三只眼”
    软件就是未来:工业4.0时代下的工业软件
    软件:工业的未来
    poj 3422 Kaka's Matrix Travels 费用流
  • 原文地址:https://www.cnblogs.com/yuanchaoyong/p/15753743.html
Copyright © 2011-2022 走看看