zoukankan      html  css  js  c++  java
  • Comparable和Comparator的区别&Collections.sort的两种用法

      在Java集合的学习中,我们明白了:

        看到tree,可以按顺序进行排列,就要想到两个接口。Comparable(集合中元素实现这个接口,元素自身具备可比性),Comparator(比较器,传入容器构造方法中,容器具备可比性)。

    那么Comparable和Comparator有什么区别呢?

    1.  Comparable---接口(集合中元素实现此接口,元素具有可比性)

      Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现,compareTo方法也被称为自然比较方法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo(Object o)方法的返回值是int,且此方法只有一个参数,返回值有三种情况:

    1、返回正整数

    2、返回0

    3、返回负整数

      可以这么理解:返回1表示当前元素排在与之对比的元素后面,返回-1表示当前元素排在与之对比的元素前面,返回0表示不排序(按其原顺序排列)。(其实并不是1,-1,0;只要是正数负数和0就可以进行区分)。

      元素自身可以理解为基准,而参数上的obj可以理解为与之对比的元素。

    1.比如我们想比较人的时候按年龄倒序排列

      思路:实现上面接口,如果与之对比的元素年龄比他大,排在他前面(返回负数),否则排在他后面(返回正数)。

    例如:

    public class Person implements Comparable<Person> {
    
        private int age;
        private String name;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public int compareTo(Person comparedUser) {
            // 返回负数表示this对象排在comparedUser前面
            if (this.age > comparedUser.getAge()) {
                return -1;
            }
    
            // 返回正数表示this对象排在comparedUser后面
            if (this.age < comparedUser.getAge()) {
                return 1;
            }
    
            return 0;
        }
    
        public Person(int age, String name) {
            super();
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person [age=" + age + ", name=" + name + "]";
        }
    
    }

    测试:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    
    public class Test1 {
        public static void main(String[] args) {
            List<Person> list = new ArrayList<>();
            list.add(new Person(20, "张三"));
            list.add(new Person(22, "李四"));
            list.add(new Person(19, "王五"));
            list.add(new Person(19, "张是"));
            list.add(new Person(17, "开发"));
            list.add(new Person(29, "看"));
            
            Collections.sort(list);
            for(Person p : list){
                System.out.println(p);
            }
        }
    }

    结果:

    Person [age=29, name=看]
    Person [age=22, name=李四]
    Person [age=20, name=张三]
    Person [age=19, name=王五]
    Person [age=19, name=张是]
    Person [age=17, name=开发]

    2.按照上面的思路我们写一个按年龄正序排列

    修改上面的compareTo方法:

        @Override
        public int compareTo(Person comparedUser) {
            // 返回负数表示this对象排在comparedUser前面
            if (this.age < comparedUser.getAge()) {
                return -1;
            }
    
            // 返回正数表示this对象排在comparedUser后面
            if (this.age > comparedUser.getAge()) {
                return 1;
            }
    
            return 0;
        } 

    测试代码还是上面代码,查看结果:

    Person [age=17, name=开发]
    Person [age=19, name=王五]
    Person [age=19, name=张是]
    Person [age=20, name=张三]
    Person [age=22, name=李四]
    Person [age=29, name=看]

    3.将自身具有可比性的元素存入TreeSet或者TreeMap进行查看(TreeSet会自动将元素排序,元素作为key的时候TreeMap会根据key排序)

    Person.java

    public class Person implements Comparable<Person> {
    
        private int age;
        private String name;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public int compareTo(Person comparedUser) {
            // 返回负数表示this对象排在comparedUser前面
            if (this.age > comparedUser.getAge()) {
                return -1;
            }
    
            // 返回正数表示this对象排在comparedUser后面
            if (this.age < comparedUser.getAge()) {
                return 1;
            }
    
            return 0;
        }
    
        public Person(int age, String name) {
            super();
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person [age=" + age + ", name=" + name + "]";
        }
    
    }

    测试代码:

    package cn.qlq.test;
    
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeMap;
    import java.util.TreeSet;
    
    
    public class TreeSetTest {
        public static void main(String[] args) {
            Set<Person> list = new TreeSet<>();
            list.add(new Person(20, "张三"));
            list.add(new Person(22, "李四"));
            list.add(new Person(19, "王五"));
            list.add(new Person(19, "张是"));
            list.add(new Person(17, "开发"));
            list.add(new Person(29, "看"));
            
            for(Person p : list){
                System.out.println(p);
            }
            System.out.println("-----------------------------");
            
            //作为key会将key排序
            Map map = new TreeMap();
            map.put(new Person(20, "张三"), "1");
            map.put(new Person(22, "李四"), "2");
            map.put(new Person(19, "张三"), "3");
            map.put(new Person(25, "哇塞"), "4");
            for(Object key :map.keySet()){
                System.out.println(key+"	"+map.get(key));
            }
            
            System.out.println("-----------------------------");
            //作为value无效
            Map map2 = new TreeMap();
            map2.put("1",new Person(20, "张三"));
            map2.put("2",new Person(22, "李四"));
            map2.put("3",new Person(19, "张三"));
            map2.put("4",new Person(25, "哇塞"));
            for(Object key :map2.keySet()){
                System.out.println(key+"	"+map2.get(key));
            }
        }
    }

    结果:

    Person [age=29, name=看]
    Person [age=22, name=李四]
    Person [age=20, name=张三]
    Person [age=19, name=王五]
    Person [age=17, name=开发]
    -----------------------------
    Person [age=25, name=哇塞] 4
    Person [age=22, name=李四] 2
    Person [age=20, name=张三] 1
    Person [age=19, name=张三] 3
    -----------------------------
    1 Person [age=20, name=张三]
    2 Person [age=22, name=李四]
    3 Person [age=19, name=张三]
    4 Person [age=25, name=哇塞]

    2.  Comparator---接口(可以理解为比较器,给集合传递比较器集合具有可比性)

      Comparator相当于外部比较器,其作为参数传给具有可比性的集合,使集合具有可比性。比如:      TreeSet ts = new TreeSet(new MyComparator());   比较器需要重写compareTo(Object o1,Object o2)方法,返回值也是下面三个值:

    1、返回正整数

    2、返回0

    3、返回负整数

      可以这么理解:返回1表示当前元素排在与之对比的元素后面,返回-1表示当前元素排在与之对比的元素前面,返回0表示不排序(按其原顺序排列)。(其实并不是1,-1,0;只要是正数负数和0就可以进行区分)

       compareTo(Object o1,Object o2)方法的第一个参数可以理解为基准,而参数上的第二个参数可以理解为与之对比的元素。

    1.比如我们想比较人的时候按年龄倒序排列

    public class Person{
    
        private int age;
        private String name;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Person(int age, String name) {
            super();
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person [age=" + age + ", name=" + name + "]";
        }
        
    
    }

    比较器类:(可以理解为第一个参数是基准,第二个参数是与之对比的元素,返回-1表示基准排在与之对比元素前面,返回1表示基准在与之对比的元素后面)

    import java.util.Comparator;
    
    public class PersonComparator implements Comparator<Person> {
    
        /**
         * 第一个参数可以理解为基准,第二个是与之比较的元素 。返回负数表示排在其前面,返回正数表示排在其后面
         */
        @Override
        public int compare(Person o1, Person o2) {
            // 返回负数表示o1排在o2前面
            if (o1.getAge() > o2.getAge()) {
                return -1;
            }
    
            // 返回正数表示o1排在o2后面
            if (o1.getAge() < o2.getAge()) {
                return 1;
            }
            return 0;
        }
    
    }

    测试代码:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    
    public class Test1 {
        public static void main(String[] args) {
            List<Person> list = new ArrayList<>();
            list.add(new Person(20, "张三"));
            list.add(new Person(22, "李四"));
            list.add(new Person(19, "王五"));
            list.add(new Person(19, "张是"));
            list.add(new Person(17, "开发"));
            list.add(new Person(29, "看"));
            
            Collections.sort(list,new PersonComparator());
            for(Person p : list){
                System.out.println(p);
            }
        }
    }

    结果:

    Person [age=29, name=看]
    Person [age=22, name=李四]
    Person [age=20, name=张三]
    Person [age=19, name=王五]
    Person [age=19, name=张是]
    Person [age=17, name=开发]

    2.写一个正序的比较器就简单了

    (1)直接调用工具类反转比较器实现一个正序比较器:

    Comparator<Person> reverseOrder = Collections.reverseOrder(new PersonComparator());

    其内部是构造了一个反向比较器:(将基准更换)

        private static class ReverseComparator2<T> implements Comparator<T>,
            Serializable
        {
            private static final long serialVersionUID = 4374092139857L;
    
            final Comparator<T> cmp;
    
            ReverseComparator2(Comparator<T> cmp) {
                assert cmp != null;
                this.cmp = cmp;
            }
    
            public int compare(T t1, T t2) {
                return cmp.compare(t2, t1);
            }
            ...
    }

    (2)修改比较器代码:

    import java.util.Comparator;
    
    public class PersonComparator implements Comparator<Person> {
    
        /**
         * 第一个参数可以理解为基准,第二个是与之比较的元素 。返回负数表示排在其前面,返回正数表示排在其后面
         */
        @Override
        public int compare(Person o1, Person o2) {
            // 返回负数表示o1排在o2前面
            if (o1.getAge() < o2.getAge()) {
                return -1;
            }
    
            // 返回正数表示o1排在o2后面
            if (o1.getAge() > o2.getAge()) {
                return 1;
            }
            return 0;
        }
    
    }

    3.将一个比较器传入TreeSet或者TreeMap是集合有序,或者TreeMap的key值有序(也就是TreeMap排序是将key排序)

      TreeSet中的元素会自动排序,根据传下来的比较器对里面的元素进行排序。TreeMap传入比较器的话是元素作为key才可以排序,如果传入比较器但是key不是比较器指定的元素会报错。。。。。。。

    比较器:(正序比较器)

    import java.util.Comparator;
    
    public class PersonComparator implements Comparator<Person> {
    
        /**
         * 第一个参数可以理解为基准,第二个是与之比较的元素 。 返回正数表示o1排在o2后面,返回负数表示o1排在o2前面
         */
        @Override
        public int compare(Person o1, Person o2) {
            if (o1.getAge() > o2.getAge()) {
                return 1;
            }
    
            if (o1.getAge() < o2.getAge()) {
                return -1;
            }
            return 0;
        }
    
    }

    测试代码:

    package cn.qlq.test;
    
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeMap;
    import java.util.TreeSet;
    
    
    public class TreeSetTest {
        public static void main(String[] args) {
            Set<Person> list = new TreeSet<>(new PersonComparator());
            list.add(new Person(20, "张三"));
            list.add(new Person(22, "李四"));
            list.add(new Person(19, "王五"));
            list.add(new Person(19, "张是"));
            list.add(new Person(17, "开发"));
            list.add(new Person(29, "看"));
            
            for(Person p : list){
                System.out.println(p);
            }
            System.out.println("-----------------------------");
            
            //作为key不会报错,会将key排序
            Map map = new TreeMap(new PersonComparator());
            map.put(new Person(20, "张三"), "1");
            map.put(new Person(22, "李四"), "2");
            map.put(new Person(19, "张三"), "3");
            map.put(new Person(25, "哇塞"), "4");
            for(Object key :map.keySet()){
                System.out.println(key+"	"+map.get(key));
            }
            
            //作为value报错
            Map map2 = new TreeMap(new PersonComparator());
            map2.put("1",new Person(20, "张三"));
            map2.put("2",new Person(22, "李四"));
            map2.put("3",new Person(19, "张三"));
            map2.put("4",new Person(25, "哇塞"));
            for(Object key :map2.keySet()){
                System.out.println(key+"	"+map2.get(key));
            }
        }
    }

    结果:

    Person [age=17, name=开发]
    Person [age=19, name=王五]
    Person [age=20, name=张三]
    Person [age=22, name=李四]
    Person [age=29, name=看]
    -----------------------------
    Person [age=19, name=张三] 3
    Person [age=20, name=张三] 1
    Person [age=22, name=李四] 2
    Person [age=25, name=哇塞] 4
    -----------------------------
    Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to cn.qlq.test.Person
    at cn.qlq.test.PersonComparator.compare(PersonComparator.java:1)
    at java.util.TreeMap.compare(Unknown Source)
    at java.util.TreeMap.put(Unknown Source)
    at cn.qlq.test.TreeSetTest.main(TreeSetTest.java:37)

    补充:Collections.sort的两种用法

      Collections.sort有两种用法,第一个是只传递一个list参数,第二个是传递两个参数(list,Comparator):

        public static <T extends Comparable<? super T>> void sort(List<T> list) {
            Object[] a = list.toArray();
            Arrays.sort(a);
            ListIterator<T> i = list.listIterator();
            for (int j=0; j<a.length; j++) {
                i.next();
                i.set((T)a[j]);
            }
        }
    
        public static <T> void sort(List<T> list, Comparator<? super T> c) {
            Object[] a = list.toArray();
            Arrays.sort(a, (Comparator)c);
            ListIterator i = list.listIterator();
            for (int j=0; j<a.length; j++) {
                i.next();
                i.set(a[j]);
            }
        }

    (1)Collections.sort(list)默认采用升序排列;  Collections.reverse(list) 是对集合进行反转

    package cn.xm.exam.test;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class Test2 {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            list.add(5);
            list.add(4);
            list.add(6);
            list.add(3);
            list.add(1);
            System.out.println(list);
            System.out.println("===================");
            Collections.sort(list);
            System.out.println(list);
        }
    }

    结果:

    [5, 4, 6, 3, 1]
    ===================
    [1, 3, 4, 5, 6]

    如果想要降序排列可以先正序排列之后再反转即可:

            Collections.sort(list); //正序排列
            Collections.reverse(list);//反转集合

    (2)Collections.sort(list, Comparator);可以传入一个比较器,也可以将比较器反转

    package cn.xm.exam.test;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class Test2 {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            list.add(5);
            list.add(4);
            list.add(6);
            list.add(3);
            list.add(1);
            System.out.println(list);
            System.out.println("===================");
            Collections.sort(list, new IntegerComparator());
            System.out.println(list);
        }
    }
    
    /**
     * 一个逆序排列的比较器
     * 
     * @author Administrator
     *
     */
    class IntegerComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            if (o1 > o2) {
                return -1;// 大的排在前面
            } else if (o1 < o2) {
                return 1;// 小的排在后面
            }
            return 0;
        }
    }

    结果:

    [5, 4, 6, 3, 1]
    ===================
    [6, 5, 4, 3, 1]

    当然了,比较器也可以反转之后再次使用,例如:

            List<Integer> list = new ArrayList<>();
            list.add(5);
            list.add(4);
            list.add(6);
            list.add(3);
            list.add(1);
            System.out.println(list);
    
            System.out.println("===================");
            Comparator<Integer> reverseOrder = Collections.reverseOrder(new IntegerComparator());// 将比较器反转
            Collections.sort(list, reverseOrder);// 用反转后的比较器排序
    
            System.out.println(list);

    结果:

    [5, 4, 6, 3, 1]
    ===================
    [1, 3, 4, 5, 6]

  • 相关阅读:
    python练习册 0002随机生成验证
    女生应该找什么样的男生
    博弈论 尼姆博弈,人生第一道博弈纪念一下
    博弈论版版
    博弈论
    STL用法大全
    据说是Flord算法
    20190716-T3-奇袭
    20190716-T1-礼物
    20190716-被水题淹没
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/9434139.html
Copyright © 2011-2022 走看看