Java中的Comparable与Comparator

1.Comparable

Comparable是排序接口。此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo()方法被称为它的自然比较方法。

如果一个类实现了Comparable接口,就意味着该类”支持排序”。实现此接口的对象列表(或数组)可以通过Collections.sort()(或Arrays.sort())进行自动排序。实现此接口的对象可以用作有序映射中的键(比如TreeMap中的键)或者有序集合中的元素(比如TreeSet中的元素),而无需指定比较器。

Comparable接口仅仅只包括一个compareTo(Object obj)函数,如下所示:

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

假设我们通过x.compareTo(y)来比较xy的大小。若该方法返回正整数,则意味着x > y;若该方法返回负整数,则意味着x < y;若该方法返回0,则意味着x == y

2.Comparator

Comparator是比较器接口。我们可以将Comparator传递给sort方法(比如Collections.sort()或者Arrays.sort()),从而对一个Collection实现精准排序。还可以通过Comparator来控制某些数据结构(比如TreeSet或者TreeMap)的顺序。

也就是说,我们如果要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。

Comparator接口包括int compare(T o1,T o2)equals()两个函数:

public interface Comparator<T>{
    int compare(T o1, T o2);

    boolean equals(Object obj);
}

要知道的是:

  • (1)若一个类实现了Comparator接口,它一定要实现compare(T o1,T o2)方法,但可以不实现equals(Object obj)方法。为什么呢?因为任何类都默认继承于Object类,在Object类中实现了equals()方法,所以相当于其他所有的类都已经实现了equals()方法。
  • (2)int compare(T o1,T o2)方法是比较o1o2的大小,该方法返回正整数,意味着o1o2大;返回负整数,意味着o1o2小;返回0,意味着o1o2相等。

3.Comparable与Comparator的比较

Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”
Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。

我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

下面我们举一个例子。


首先我们自定义一个People类,如下:

public class People implements Comparable<People>{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    /**
     * 两个People对象的name属性和age属性都相等,就认为两个对象相等
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        People other = (People) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return name + "---" + age;
    }

    /**
     *  默认的排序,按照name属性排序
     */
    @Override
    public int compareTo(People o) {
        return this.name.compareTo(o.name);
    }

}

People类有nameage两个属性,重写了equals()hashCode()方法,并且重写了toString()方法。最重要的是,People类实现了Comparable接口,并重写了compareTo(People o)方法,该方法让People类默认支持排序,并且排序的依据是name属性的字母序。


接着,我们可以在主方法中测试:

public class Test {
    public static void main(String[] args) {
        System.out.println("----------ArrayList------------");
        ArrayList<People> array = new ArrayList<>();
        array.add(new People("ddd",20));
        array.add(new People("aaa",30));
        array.add(new People("ccc",10));
        array.add(new People("bbb",40));

        System.out.println("原始序列:"+array);

        // 对array进行排序,按照People类默认的排序方法,即按照name属性进行升序排列
        Collections.sort(array);
        System.out.println("Name升序:"+array);
    }
}

程序输出:

原始序列:[ddd---20, aaa---30, ccc---10, bbb---40]
Name升序:[aaa---30, bbb---40, ccc---10, ddd---20]

可以看到,我们采用ArrayList保存了4个People类对象,并调用Collections.sotr()方法对该List进行排序,排序之前是原始序列;排序之后,按照People类中的定义,默认按照name属性的升序排列。


除此之外,我们可以自定义Comparator类对象,使之按照age属性进行排列。

比如,我们可以按照age属性的升序排列:

public class AscAgeComparator implements Comparator<People> {
    @Override
    public int compare(People o1, People o2) {
        return o1.getAge() - o2.getAge();
    }
}

自定义AscAgeComparator类,实现Comparator接口,并重写compare(People o1, People o2)方法,该方法按照age属性进行升序排列。

或者我们也可以按照age属性进行降序排列,如下定义DescAgeComparator:

public class DescAgeComparator implements Comparator<People> {
    @Override
    public int compare(People o1, People o2) {
        return o2.getAge() - o1.getAge();
    }
}

这时候我们在调用Collections.sort()方法时,也可以给该方法传递一个Comparator对象,使之按照该对象自定义的排序方式进行排序:

public class Test {
    public static void main(String[] args) {
        System.out.println("----------ArrayList------------");
        ArrayList<People> array = new ArrayList<>();
        array.add(new People("ddd",20));
        array.add(new People("aaa",30));
        array.add(new People("ccc",10));
        array.add(new People("bbb",40));

        System.out.println("原始序列:"+array);

        // 通过AscAgeComparator比较器进行排序,根据age升序排列
        Collections.sort(array, new AscAgeComparator());
        System.out.println("Age升序:"+array);

        // 通过DescAgeComparator比较器进行排序,根据age降序排列
        Collections.sort(array, new DescAgeComparator());
        System.out.println("Age降序:"+array);
    }
}

程序输出:

原始序列:[ddd---20, aaa---30, ccc---10, bbb---40]
Age升序:[ccc---10, ddd---20, aaa---30, bbb---40]
Age降序:[bbb---40, aaa---30, ddd---20, ccc---10]

4.Comparator的应用之实现Map按照value值进行排序

这里补充一个Comparator的一个很实用的应用例子。

Map本身并不支持按照value值进行排序,TreeMap实现的也是按照key值进行排序,那么如何实现Map按照value值进行排序呢?这时候我们可以将该Map转换成List<Map.Entry<T,T>>,也就是把Map中每一个Entry键值对保存在List中,再自定义Comparator,对该List进行排序即可。

比如,我们再自定义People类如下:

public class People {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }
    public String getName() {
        return name;
    }
    public People( String name,int age) {
        super();
        this.age = age;
        this.name = name;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        People other = (People) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return name + "---" + age;
    }

}

这次,People类并没有实现Comparable接口,也就是说People类不支持排序。

我们将People类对象作为value值保存在Map中,做如下测试:

public class Test {
    public static void main(String[] args) {
        HashMap<String, People> hm = new HashMap<>();
        hm.put("1", new People("ddd",20));
        hm.put("2", new People("ccc",40));
        hm.put("3", new People("aaa",30));
        hm.put("4", new People("bbb",10));

        System.out.println("排序之前:");
        hm.forEach((key,value) -> System.out.println(key + "  " + value));

        // 将Map中的每一个键值对转化为Entry对象,保存在List中
        List<Entry<String, People>> list = new ArrayList<>(hm.entrySet());

        // 自定义Comparator,按照People对象的年龄升序进行排序
        Comparator<Map.Entry<String, People>> valueComparator  = new Comparator<Map.Entry<String,People>>() {
            @Override
            public int compare(Entry<String, People> o1, Entry<String, People> o2) {
                return o1.getValue().getAge() - o2.getValue().getAge();
            }
        };

        // 排序,比较器为我们自定义的valueComparator
        Collections.sort(list, valueComparator);

        System.out.println("排序之后:");
        //Java8中支持的遍历输出:
        list.forEach((item -> System.out.println(item.getKey() + "  " + item.getValue())));
    }
}

最后我们按照People对象的age属性进行升序排列,程序输出:

排序之前:
1  ddd---20
2  ccc---40
3  aaa---30
4  bbb---10
排序之后:
4  bbb---10
1  ddd---20
3  aaa---30
2  ccc---40
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值