Comparator的用法

2,318 阅读3分钟

1.为什么写?

  1. comparator是javase中的接口,位于java.util包下,该接口抽象度极高,有必要掌握该接口的使用。 2.大多数文章告诉大家comparator是用来排序,但是排序只是comparator能实现的功能之一,不仅限于排序。

2. 接口功能

该接口代表一个比较器,比较器具有可比性。大多数文章都写如何用comparator排序,是因为javase数组工具类和集合工具类中提供的sort方法,sort就是使用Comparator接口来处理排序的,大家见久了都认为Comparator接口就是用来排序的,按照java抽象的尿性来看,该接口如果为排序而生,应该叫Sortable,Sortor之类的名字吧。下面是javaSE一些使用到的Comparator接口的地方:

1. Arrays.sort(T[],Comparator<? super T> c);
2. Collections.sort(List<T> list,Comparator<? super T> c);

3. 使用场景

什么场景需要作比较,那么什么场景就是Comparator接口的用武之地,我总结的两个场景:

  • 排序:需要比较两个对象谁排在前面谁排在后面(排序也可以让类实现Comparable接口,实现后该类的实例也具有排序的功能)。
  • 分组:需要比较两个对象是否属于同一个组

4. 举例子

排序:

在List或数组中的对象如果没有实现Comparable接口的时候,那么就需要调用者为需要排序的数组或者List设置一个Comparator接口,Comparator的compare方法用来告诉代码应该如何来比较两个实例,然后再根据比较结果进行排序。

package ddw.test.comparator;

import ddw.test.Collections.Demo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<Dog> list = new ArrayList<>();
        list.add(new Dog(5, "DogA"));
        list.add(new Dog(6, "DogB"));
        list.add(new Dog(7, "DogC"));
        Collections.sort(list, new Comparator<Dog>() {
            @Override
            public int compare(Dog o1, Dog o2) {
                return o2.age - o1.age;
            }
        });

        System.out.println("按照狗狗的年龄倒序:" + list);


        Collections.sort(list, new Comparator<Dog>() {
            @Override
            public int compare(Dog o1, Dog o2) {
                return o1.name.compareTo(o2.name);
            }
        });


        System.out.println("按照狗狗的名字的字母顺序排序:" + list);
    }
}


class Dog {
    public int age;
    public String name;

    public Dog(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }


}
  1. 分组

使用Comparator和for循环处理列表,来进行分类,通过调用者实现Comparator接口的比较逻辑,来告诉程序应该怎么比较,通过比较之后得到的结果来进行分组。比如生活中的拳击比赛,会有公斤级的概念,那么程序中应该实现的处理逻辑是只要两个人的体重在同一个区间则为同一组公斤级选手。下面这个例子分别按照苹果的颜色和体重级别两个维度来进行分组,因此分组的核心逻辑是比较逻辑。下面我抽取了一个工具方法:dividerList,第一个参数为需要处理的数据源,第二个参数是分组的时候比较逻辑。

package ddw.test.comparator;

import java.util.*;

public class demo2 {
    public static void main(String[] args) {
        List<Apple> list = new ArrayList<>();
        list.add(new Apple("红", 205));
        list.add(new Apple("红", 131));
        list.add(new Apple("绿", 248));
        list.add(new Apple("绿", 153));
        list.add(new Apple("黄", 119));
        list.add(new Apple("黄", 224));


        List<List<Apple>> byColors=divider(list, new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                //按照颜色分组
                return o1.color.compareTo(o2.color);
            }
        });

        System.out.println("按颜色分组" + byColors);




        List<List<Apple>> byWeight=divider(list, new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                //按照重量级
                return (o1.weight / 100 == o2.weight / 100 ? 0 : 1);
            }
        });

        System.out.println("按重量级分组:" + byWeight);
    }


    public static <T> List<List<T>> divider(Collection<T> datas, Comparator<? super T> c) {
        List<List<T>> result = new ArrayList<>();
        for (T t : datas) {
            boolean isSameGroup = false;
            for (int j = 0; j < result.size(); j++) {
                if (c.compare(t, result.get(j).get(0)) == 0) {
                    isSameGroup = true;
                    result.get(j).add(t);
                    break;

                }
            }

            if (!isSameGroup) {
                //创建
                List<T> innerList = new ArrayList<>();
                result.add(innerList);
                innerList.add(t);
            }

        }
        return result;



    }

}

class Apple {
    public String color;
    public int weight;

    public Apple(String color, int weight) {
        this.color = color;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "color='" + color + '\'' +
                ", weight=" + weight +
                '}';
    }
}

5. 总结

一般需要做比较复杂的逻辑都可以使用的上Comparator,最常用的场景就是排序和分组,排序尝使用的是Arrays和Collections的sort方法,而分组则可以使用上面提供的divider方法。

排序和分组的区别:

排序的时候:两个对象的比较结果有三种:大于,等于,小于

分组的时候:两个对象比较的结果只有两种,等于(两个对象属于同一组),不等于(两个对象不属于同一组)。