Contents

Comparable和Comparator

在Arrays类中的sort方法能够对对象数组进行排序,但是我们知道,排序要有排序规则,如果是自定义的类,必须明确这些规则才能够使用Arrays.sort方法对对象数组进行排序。实际上这些排序规则就是一种比较规则。

对于基本数据类型,例如int等,或者是常用的对象,例如String ,其本身就定义了比较规则,因此我们不需要在重新定义比较规则。但是对于我们自己定义的类就必须重新定义比较规则。

在Java中实现Arrays.sort数组排序有两种方式:

  • 实现Comparable接口,并覆写compareTo方法。在调用Arrays.sort只需要传入对象数组即可
  • 在调用Arrays.sort时需要传入一个数组和一个比较器作为参数。比较器是实现了Comparator接口中compare方法的类的的实例。

首先来看第一种方式

类实现Comparable类中的compareTo方法,Arrays.sort传入对象数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ArraysTest {
    public static void main(String[] args) {
        Employee[] employees=new Employee[3];//构建一个对象数组
        employees[0]=new Employee(10000,"xiaoming");
        employees[1]=new Employee(20000,"xiaohong");
        employees[2]=new Employee(15000,"xiaocai");

        Arrays.sort(employees);//对数组进行排序,由于此时类已经实现了Comparable接口中的comparaTo方法,所以自需要传入对象数组即可
        
        for (Employee s:employees){
            System.out.println(s.getSalary());
        }
        /*
        输出结果:
        10000
		15000
		20000
        */
    }
}


class Employee implements Comparable<Employee>{
    private int salary;
    private String name;
    public Employee(int salary,String name){
        this.salary=salary;
        this.name=name;
    }
    public int getSalary(){
        return salary;
    }


    @Override
    public int compareTo(Employee o) {
        return Integer.compare(salary,o.salary);//按照薪水进行比较
    }
}

第二种方式

在调用Arrays.sort时需要传入一个数组和一个比较器作为参数。比较器是实现了Comparator接口中compare方法的类的的实例。

这听起来有一些拗口,来看一看代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ArraysTest {
    public static void main(String[] args) {
        Employee[] employees=new Employee[3];//构建一个对象数组
        employees[0]=new Employee(10000,"xiaoming");
        employees[1]=new Employee(20000,"xiaohong");
        employees[2]=new Employee(15000,"xiaocai");

        Arrays.sort(employees,new com());//对数组进行排序,此时传入的参数为对象数组以及比较器。
        
        for (Employee s:employees){
            System.out.println(s.getSalary());
        }
        /*
        输出结果:
        10000
		15000
		20000
        */
    }
}


class Employee {
    private int salary;
    private String name;
    public Employee(int salary,String name){
        this.salary=salary;
        this.name=name;
    }
    public int getSalary(){
        return salary;
    }

    
class com implements Comparator<Employee1>{//实现Comparator接口并覆写compare方法
    @Override
    public int compare(Employee1 o1, Employee1 o2) {
        return Integer.compare(o1.getSalary(), o2.getSalary());
    }
}
    

从代码中可以看出,实际上比较器就是实现了Comparator接口的实现类的一个对象。这一个对象也可以采用匿名内部类的方式来表示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ArraysTest {
    public static void main(String[] args) {
        Employee[] employees=new Employee[3];//构建一个对象数组
        employees[0]=new Employee(10000,"xiaoming");
        employees[1]=new Employee(20000,"xiaohong");
        employees[2]=new Employee(15000,"xiaocai");

        Arrays.sort(employees, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return Integer.compare(o1.getSalary(), o2.getSalary());
            }
        });//对数组进行排序,此时传入的参数为对象数组以及比较器。
        
        for (Employee s:employees){
            System.out.println(s.getSalary());
        }
        /*
        输出结果:
        10000
		15000
		20000
        */
    }
}


class Employee {
    private int salary;
    private String name;
    public Employee(int salary,String name){
        this.salary=salary;
        this.name=name;
    }
    public int getSalary(){
        return salary;
    }


    

或者采用lambda表达式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ArraysTest {
    public static void main(String[] args) {
        Employee[] employees=new Employee[3];//构建一个对象数组
        employees[0]=new Employee(10000,"xiaoming");
        employees[1]=new Employee(20000,"xiaohong");
        employees[2]=new Employee(15000,"xiaocai");
 		
        Arrays.sort
                (employee1s, (
                                (o1, o2) -> {
                                    return Integer.compare(o1.getSalary(), o2.getSalary());
                                }
                        )
                );//对数组进行排序,此时传入的参数为对象数组以及比较器。
        
        for (Employee s:employees){
            System.out.println(s.getSalary());
        }
        /*
        输出结果:
        10000
		15000
		20000
        */
    }
}


class Employee {
    private int salary;
    private String name;
    public Employee(int salary,String name){
        this.salary=salary;
        this.name=name;
    }
    public int getSalary(){
        return salary;
    }


    

实际上,不仅仅是Arrays.sort方法要求传入的对象数组必须实现Comparable接口或者Arrays.sort需要传入实现Comparator接口的比较器,对于TreeSet和TreeMap中的key部分也需要实现比较规则,因为TreeSet和TreeMap的底层结果为自平衡二叉树,其特点是可排序。

来看代码

1.TreeSet中的元素或TreeMap中的key部分需要实现Comparable接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class TreeMapTest03 {
    public static void main(String[] args) {
        User u1=new User(25);
//        System.out.println(u1);
        User u2=new User(23);
        User u3=new User(30);
        User u4=new User(27);

        TreeSet<User> ts=new TreeSet<>();
        ts.add(u1);
        ts.add(u2);
        ts.add(u3);
        ts.add(u4);

        for (User it:ts){
            System.out.println(it);
        }
    }
}

class User implements Comparable<User> {//实现Comparable接口
    private int age;

    public User(int age){
        this.age=age;
    }

    public String toString(){
        return "age= "+age;
    }

    @Override
    public int compareTo(User o) {//重写CompareTo方法,o1.compareTo(o2)
        //其中this表示o1
        //o表示o2
        // o1和o2比较时就是this和o比较

//        int age1=this.age;
//        int age2=o.age;
//        if (age1==age2){
//            return 0;
//        }else if (age1>age2){
//            return 1;
//        }else{
//            return -1;
//        }


        return this.age-o.age;

        /*
        * 根据返回的正负数就可以进行排序
        * 为什么根据返回的正负数就可以进行排序?
        * 这是因为二叉树的数据结构(TreeSet的底层数据结构为二叉树)
        *
        * */
    }
}

2.给构造方法传入一个比较器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class TreeMapTest05 {
    public static void main(String[] args) {
        //给构造方法传递一个比较器
        TreeSet<wugui> wuguis =new TreeSet<>(new wuguicompare());

        //或者使用匿名内部类:这个类没有名字,直接new接口
//        TreeSet<wugui> wuguis=new TreeSet<>(new Comparator<wugui>() {
//            @Override
//            public int compare(wugui o1, wugui o2) {
//                return o1.age-o2.age;
//            }
//        });


        wugui w1=new wugui(500);
        wugui w2=new wugui(200);
        wugui w3=new wugui(700);

        wuguis.add(w1);
        wuguis.add(w2);
        wuguis.add(w3);

        for (wugui it:wuguis){
            System.out.println(it);
        }

    }
}

class wugui{
    public int age;
    public wugui(int age){
        this.age=age;
    }

    @Override
    public String toString() {
        return "wugui{" +
                "age=" + age +
                '}';
    }
}

class wuguicompare implements Comparator<wugui>{

    @Override
    //自定义比较规则
    public int compare(wugui o1, wugui o2) {
        return o1.age-o2.age;
    }
}
1
2
3
4
5
6
7
8
结论:
*   放到TreeSet或者TrssMap集合中的元素要想实现排序,包括两种方式:
*       1.放在集合中的元素要实现java.lang.Comparable接口(重写CompareTo方法)
*       2.在构造TreeSet或TreeMap集合的时候给它传一个比较器对象(实现Comparator接口)。
*
* Comparable和Comparator如何选择?
*   当比较规则不发生改变时,或者说比较规则只有一个时,建议选择Comparable
*   如果比较规则有多个或者比较规则之间需要频繁切换,建议选择Comparator

Collections.sort

Collections.sort能够给List list进行排序,其中集合需要自定义比较规则,也是一样的两种方式 –>Arrays.sort是对数组进行排序

  • 实现Comparable接口,并覆写compareTo方法。在调用Collections.sort只需要传入集合即可
  • 在调用Collections.sort时需要传入一个数组和一个比较器作为参数。比较器是实现了Comparator接口中compare方法的类的的实例。

HashMap按照key或value进行排序

参考

1
2
3
4
5
6
7
8
9
        Map phone=new HashMap();
        phone.put("Apple",7299);
        phone.put("SAMSUNG",6000);
        phone.put("Meizu",2698);
        phone.put("Xiaomi",2400);
        System.out.println(phone);
/*
{Meizu=2698, Apple=7299, Xiaomi=2400, SAMSUNG=6000}
*/

按照key进行排序:

对key进行排序,首先要得到HashMap中键的集合(keySet),并转换为数组,这样才能用Arrays.sort()进行排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
        Set set=phone.keySet();//得到key集合
        Object[] arr=set.toArray();//将key集合转换为数组
        Arrays.sort(arr);//对数组进行排序
        for(Object key:arr){
            System.out.println(key);
        }


        for(Object key:arr){
            System.out.println(key+": "+phone.get(key));
        }


按照value进行排序

将entrySet转换为List,然后重写比较器比较即可.这里可以使用List.sort(comparator),也可以使用Collections.sort(list,comparato

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//将将entrySet转换为List
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(phone.entrySet()); //转换为list

//重写比较器,使用list.sort()排序
       list.sort(new Comparator<Map.Entry<String, Integer>>() {
          @Override
          public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
              return o2.getValue().compareTo(o1.getValue());//对value进行排序
          }
      });

//或者使用Collections.sort()排序
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
           @Override
           public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
               return o2.getValue().compareTo(o1.getValue());
           }
       });



//for-each循环
           for (Map.Entry<String, Integer> mapping : list){
            System.out.println(mapping.getKey()+": "+mapping.getValue());
        }