• 玩转 Comparator 和 Comparable 两接口


    最近项目中有排序的需求,就查看了一下Java文档,发现有两个接口都可以进行排序,Comparable 和 Comparator 两接口到底有啥区别?何时用?怎么用?使用场景我都在底下一一研究分享出来:

    一、Comparable 比较器

    (1)Comparable 是接口,可以认为是一个内比较器,实现了Comparable 接口的类有一个特点,就是这些类可以和自己进行比较,比较逻辑依赖于 comparaTo() 方法。如果借用Collections.sort() 方法来进行排序,那么这个类必须实现 Comparable 接口并实现 compareTo() 方法,java的很多类都实现了Comparable接口,比如 String、Integer 等类

    public interface Comparable<T> {
            public int compareTo(T o);
    }
    

      调用此方法,也就是同一个List中的同类型元素进行比较,即this和o比较;若返回值大于0则this > o,返回值等于0则是this = o,返回值小于0则是this < o;

    (2)实例代码:

    public class UserComparable implements Comparable<UserComparable>{
    
        private static Logger logger = LoggerFactory.getLogger(UserComparable.class);
    
        private String name;
    
        private Integer age;
    
        public UserComparable(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public int compareTo(UserComparable user) {
            if (this.age.compareTo(user.getAge()) > 0) {
                return 1;
            } else if (this.age.compareTo(user.getAge()) == 0) {
                return 0;
            } else {
                return -1;
            }
        }
    
        @Override
        public String toString() {
            return "name = " + this.getName() + ", age = " + this.getAge();
        }
    
        public static void main(String[] args) {
            List<UserComparable> userList = Lists.newArrayList();
            userList.add(new UserComparable("xiaoxiao", 22));
            userList.add(new UserComparable("honghong", 19));
            userList.add(new UserComparable("mingming", 29));
            userList.add(new UserComparable("shuishui", 26));
            userList.add(new UserComparable("yangyang", 34));
            //排序前
            logger.info("排序前");
            userList.stream().forEach(user -> System.out.println(user.toString()));
            //排序后
            logger.info("排序后");
            Collections.sort(userList);
            userList.stream().forEach(user -> System.out.println(user.toString()));
        }
    
    }

    (3)执行结果:

    17:25:59.511 [main] INFO com.springboot.base.comparable.UserComparable - 排序前
    name = xiaoxiao, age = 22
    name = honghong, age = 19
    name = mingming, age = 29
    name = shuishui, age = 26
    name = yangyang, age = 34
    17:25:59.596 [main] INFO com.springboot.base.comparable.UserComparable - 排序后
    name = honghong, age = 19
    name = xiaoxiao, age = 22
    name = shuishui, age = 26
    name = mingming, age = 29
    name = yangyang, age = 34
    

    (4)总结:  

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
            list.sort(null);
        }
    

      Collections.sort();如若借助这个方法进行排序,List集合存储的元素必须是实现 Comparable 接口并重写compareTo()方法的对象;要求在此类中实现 compareTo() 接口,耦合性比较高,不建议使用!

    二、Comparator比较器

    (1)Comparator接口是一个函数式接口,只有一个抽象方法 compare(),compare比较的o1和o2,返回值大于0则o1大于o2,以此类推;

    @FunctionalInterface
    public interface Comparator<T> {
    
        /**
         * 唯一抽象方法
         */
        int compare(T o1, T o2);
    
        /**
         * 列表逆序
         */
        default java.util.Comparator<T> reversed() {
            return Collections.reverseOrder(this);
        }
    
        /**
         * 静态方法
         */
        public static <T, U> java.util.Comparator<T> comparing(
                Function<? super T, ? extends U> keyExtractor,
                java.util.Comparator<? super U> keyComparator)
        {
            Objects.requireNonNull(keyExtractor);
            Objects.requireNonNull(keyComparator);
            return (java.util.Comparator<T> & Serializable)
                    (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                            keyExtractor.apply(c2));
        }
    }  

    (2)实例代码:

    public class User {
    
        private static Logger logger = LoggerFactory.getLogger(User.class);
    
        private String name;
    
        private Integer age;
    
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "name = " + this.getName() + ", age = " + this.getAge();
        }
    
        public static void main(String[] args) {
            List<User> userList = Lists.newArrayList();
            userList.add(new User("xiaoxiao", 22));
            userList.add(new User("honghong", 19));
            userList.add(new User("mingming", 29));
            userList.add(new User("shuishui", 26));
            userList.add(new User("yangyang", 34));
            //排序前
            logger.info("升序排序前");
            userList.stream().forEach(user -> System.out.println(user.toString()));
            //排序后
            logger.info("升序排序后");
            Collections.sort(userList, new UserComparator());
            userList.stream().forEach(user -> System.out.println(user.toString()));
            logger.info("升序再逆序");
            Collections.reverse(userList);
            userList.stream().forEach(user -> System.out.println(user.toString()));
        }
    
    }
    
    class UserComparator implements Comparator<User> {
    
        @Override
        public int compare(User user1, User user2) {
            if (user1.getAge().compareTo(user2.getAge()) > 0) {
                return 1;
            } else if (user1.getAge().compareTo(user2.getAge()) == 0) {
                return 0;
            } else {
                return -1;
            }
        }
    }
    

    (3)执行结果:

    17:58:09.704 [main] INFO com.springboot.base.comparator.User - 升序排序前
    name = xiaoxiao, age = 22
    name = honghong, age = 19
    name = mingming, age = 29
    name = shuishui, age = 26
    name = yangyang, age = 34
    17:58:09.817 [main] INFO com.springboot.base.comparator.User - 升序排序后
    name = honghong, age = 19
    name = xiaoxiao, age = 22
    name = shuishui, age = 26
    name = mingming, age = 29
    name = yangyang, age = 34
    17:58:09.819 [main] INFO com.springboot.base.comparator.User - 升序再逆序
    name = yangyang, age = 34
    name = mingming, age = 29
    name = shuishui, age = 26
    name = xiaoxiao, age = 22
    name = honghong, age = 19
    

    (4)总结:

      Collections.sort(List list, Comparator comparator)方法,明显可以看出来第二个参数只需要传递一个实现Comparator接口,实现compare()方法的实例对象,实现类会定义排序的逻辑功能;

      优点:由于排序逻辑的实现是在要排序类(User)的外部编写,不会在要排序类(User)里面编写,这样就可以解除要排序类(User)和排序逻辑分离,降低耦合性;

    (5)由于Java8的新特性,只要满足有 @FunctionalInterface 注解,就能使用Lambda表达式简化排序代码:

         /**
             * 方式一:使用匿名内部类,创建一个实现Comparable接口的类对象,并重写compare()方法,编写排序逻辑
             */
            userList.stream().sorted(new Comparator<User>() {
                @Override
                public int compare(User user1, User user2) {
                    if (user1.getAge().compareTo(user2.getAge()) > 0) {
                        return 1;
                    } else if (user1.getAge().compareTo(user2.getAge()) == 0) {
                       return 0;
                    } else {
                        return -1;
                    }
                }
            }).collect(Collectors.toList());
            userList.stream().forEach(user -> System.out.println(user.toString()));
    
            /**
             * 方式二:使用Lambda表达式;由于sorted()方法需要一个实现Comparator接口,并重写compare()方法的对象,
             * compare()方法接收两个参数,Lambda表达式会根据 userList 集合存储的对象类型,自动推导出 user1,user2 的类型,
             * 并传递到compare()方法中,进行排序操作(省略这些的代码,在编译时期,会自动的推导出源代码)
             */
            userList.stream().sorted((user1, user2) -> user1.getAge().compareTo(user2.getAge())).collect(Collectors.toList());
            userList.stream().forEach(user -> System.out.println(user.toString()));
    
            /**
             * 方式三:更加简化的方式二Lambda表达式,由于 Comparator 接口存在 comparing() 静态方法,接收参数,
             * 比较方式,使用类::方法对其相应对象属性值进行排序
             */
            userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
         userList.stream().forEach(user -> System.out.println(user.toString()));

      推荐排序一般还是使用 Comparator 接口,结合 Lambda 表达式进行排序,编写简单,简捷;

    Comparable 和 Comparator 总结:

      两种方法各有优劣, 用Comparable 简单, 只需实现 Comparable 接口的对象重写compareTo()方法,直接就成为一个可以比较的对象,若要修改排序逻辑,必须要修改要排序类的原代码,耦合性太高;

      用Comparator 的好处是若排序功能需要修改,不需要修改要排序类的源代码, 但需另写类实现Comparator接口重写compare()方法,可以借用Lambda表达式进行简便操作!

  • 相关阅读:
    2019/5/13 洛谷P4742 【tarjan缩点 + 拓扑dp】
    图论500题
    欧拉回路与欧拉路径
    二分图的判定
    二分图的最大匹配以及带权匹配【匈牙利算法+KM算法】
    网络流三大算法【邻接矩阵+邻接表】POJ1273
    马拉车算法,mannacher查找最长回文子串
    tarjan算法(强连通分量 + 强连通分量缩点 + 桥(割边) + 割点 + LCA)
    luogu P5774 [JSOI2016]病毒感染 线性 dp
    luguo P2519 [HAOI2011]problem a dp+贪心
  • 原文地址:https://www.cnblogs.com/blogtech/p/11162567.html
Copyright © 2020-2023  润新知