Java 集合:Comparator 接口

Java 的 Comparator 接口(java.util.Comparator)是一个用于比较两个对象的独立组件,其核心作用是为对象提供排序规则,使它们能够通过 Java 内置的排序功能进行排序。例如,对 List 集合排序时,你可以向排序方法传入一个 Comparator 实现 —— 排序过程中,集合会依赖这个 Comparator 来比较元素,从而确定最终的排列顺序。

Comparator 与 Comparable 接口的核心区别在于:Comparator 是外部比较器,独立于被比较的对象;而 Comparable 是内部比较器,由被比较对象自身实现。因此,Comparable 定义的排序规则被称为对象的 “自然排序”,而 Comparator 则是在外部为对象指定额外的排序逻辑,不影响对象本身的默认排序行为。

  

Comparator 接口定义

Comparator 接口位于 java.util 包中。接口定义如下:

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

需要注意的是,Java 的 Comparator 接口中只有一个抽象方法 compare()。该方法接收两个待比较的对象,Comparator 的实现类正是通过重写这个方法来定义具体的比较逻辑。

compare() 方法的返回值为 int 类型,其数值大小用于表示两个对象的比较结果,具体语义如下:

  • 负值表示第一个对象小于第二个对象。

  • 值0表示两个对象相等。

  • 正值表示第一个对象大于第二个对象。

请注意,在实现Java的Comparator接口时,有一个要求,即该实现必须遵循以下传递性比较特性:

如果A大于B,且B大于C,那么A也一定大于C。

  

实现 Comparator 接口

假设你有以下示例,其中的静态内部类 Spaceship 实现了 Comparable<Spaceship> 接口,现在你希望能够比较该类的实例:

package com.hxstrive.java_collection.comparable;

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

public class ComparableDemo2 {

    public static void main(String[] args) {
        List<Spaceship> list = new ArrayList<>();
        list.add(new Spaceship("A", "A001"));
        list.add(new Spaceship("B", "B099"));
        list.add(new Spaceship("B", "B010"));

        System.out.println("排序前:" + list);
        Collections.sort(list);
        System.out.println("排序后:" + list);
    }

    static class Spaceship implements Comparable<Spaceship> {
        // 飞船的级别/类型
        private String spaceshipClass = null;
        // 飞船的注册号
        private String registrationNo = null;

        public Spaceship(String spaceshipClass, String registrationNo) {
            this.spaceshipClass = spaceshipClass;
            this.registrationNo = registrationNo;
        }

        @Override
        public int compareTo(Spaceship o) {
            // 先按飞船级别(spaceshipClass)进行比较,利用String的compareTo方法实现字符串自然顺序比较
            int spaceshipClassComparison =
                    this.spaceshipClass.compareTo(o.spaceshipClass);
            // 如果级别不同,直接返回级别比较结果
            if(spaceshipClassComparison != 0) {
                return spaceshipClassComparison;
            }

            // 如果级别相同,则按注册号(registrationNo)进行比较
            return this.registrationNo.compareTo(o.registrationNo);
        }

        @Override
        public String toString() {
            return "Spaceship[" + spaceshipClass + ", " + registrationNo + "]";
        }
    }
}

注意,上面代码中的静态内部类 Spaceship 已实现 Comparable 接口,该接口会先依据 spaceshipClass 对 Spaceship 对象进行比较,再根据 registrationNo 比较。不过,如果你想使用 Comparator 来比较对象,对象是否实现 Comparable 接口并不影响。

现在假设你只想根据 registrationNo 对 Spaceship 对象进行排序,而忽略 spaceshipClass 属性。如下:

package com.hxstrive.java_collection.comparable;

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

public class ComparableDemo3 {

    public static void main(String[] args) {
        List<Spaceship> list = new ArrayList<>();
        list.add(new Spaceship("A", "A001"));
        list.add(new Spaceship("B", "B099"));
        list.add(new Spaceship("B", "B010"));

        System.out.println("排序前:" + list);
        Collections.sort(list, new SpaceshipComparator());
        System.out.println("排序后:" + list);
    }

    // 看这里,实现了 Comparator,根据 spaceshipClass 进行比较
    static class SpaceshipComparator implements Comparator<Spaceship> {
        @Override
        public int compare(Spaceship o1, Spaceship o2) {
            return o1.spaceshipClass.compareTo(o2.spaceshipClass);
        }
    }

    static class Spaceship implements Comparable<Spaceship> {
        // 飞船的级别/类型
        private String spaceshipClass = null;
        // 飞船的注册号
        private String registrationNo = null;

        public Spaceship(String spaceshipClass, String registrationNo) {
            this.spaceshipClass = spaceshipClass;
            this.registrationNo = registrationNo;
        }

        @Override
        public int compareTo(Spaceship o) {
            // 先按飞船级别(spaceshipClass)进行比较,利用String的compareTo方法实现字符串自然顺序比较
            int spaceshipClassComparison =
                    this.spaceshipClass.compareTo(o.spaceshipClass);
            // 如果级别不同,直接返回级别比较结果
            if(spaceshipClassComparison != 0) {
                return spaceshipClassComparison;
            }

            // 如果级别相同,则按注册号(registrationNo)进行比较
            return this.registrationNo.compareTo(o.registrationNo);
        }

        @Override
        public String toString() {
            return "Spaceship[" + spaceshipClass + ", " + registrationNo + "]";
        }
    }
}

运行结果:

排序前:[Spaceship[A, A001], Spaceship[B, B099], Spaceship[B, B010]]
排序后:[Spaceship[A, A001], Spaceship[B, B099], Spaceship[B, B010]]

注意,静态内部类 SpaceshipComparator 实现 Comparator 接口时,在 <> 中指定了 Spaceship 类型(即 implements Comparator<Spaceship>)。这明确了该 Comparator 实现可比较的对象类型为 Spaceship。

将 Comparator 实现的泛型类型设为 Spaceship 后,compare () 方法的参数类型就可以直接指定为 Spaceship,而非未指定泛型时(即 implements Comparator)默认的 Object 类型。

由于 Comparator 实现几乎都是专门用于比较特定类型(类)的对象,因此在实现 Comparator 时指定泛型类型是推荐的做法。

比较数字

如果你的 Comparator 实现需要比较对象中的数字,有一种简单的方法是直接计算两个数字的差值并返回结果。

假设 Spaceship 类的 registrationNo 变量是 int 类型,那么 getRegistrationNo () 方法会返回 int 值。此时,两个 Spaceship 对象的 registrationNo 可以这样比较:

package com.hxstrive.java_collection.comparable;

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

public class ComparableDemo3 {

    //...

    // 看这里,实现了 Comparator,根据 spaceshipClass 进行比较
    static class SpaceshipComparator implements Comparator<Spaceship> {
        @Override
        public int compare(Spaceship o1, Spaceship o2) {
            return o1.getRegistrationNo() - o2.getRegistrationNo();
        }
    }

    //...
}

更多信息请参考 https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html API 文档。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号