比较器Comparable

2021-05-03

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    @Override
    public String toString() {
        return "【Book】图书名称:" + title + "、图书价格:" + price + "\n";
    }
}

public class compareDemo {
    public static void main(String[] args) {
        Book[] books = new Book[] {new Book("Java从入门到项目实战", 99.8),
                new Book("Python从入门到项目实战", 89.7),
                new Book("Go语言从入门到项目实战", 96.3)};

        Arrays.sort(books);
        System.out.println(Arrays.toString(books));
    }
}

输出:
Exception in thread "main" java.lang.ClassCastException: com.sample.usefulLib.Compare.Book cannot be cast to java.lang.Comparable

在程序执行的过程中出现一个 "ClassCastException" 异常,这种异常所描述的是对象转换异常,这里提示不能将 Book 类的对象实例转为 Comparable 。

如果给定的是一个整型数组,那么要想确定数组中的元素大小关系,直接利用各种关系运算符即可,但是如果给出的是一个对象数组,对象数组里面所包含的内容是一个个堆内存的信息,那么堆内存的信息如何进行大小关系的比较?

很明显,堆内存无法直接进行大小关系的比较,如果要进行排序处理,严格意义来讲使用的是堆内存中属性的内容来进行大小关系确认,而这个属性内容的确认就必须采用比较器来支持。在 Java 里面支持两种比较器:Comparable 和 Comparator。

Comparable 比较器

这个接口里面只提供有一个核心的比较方法:compareTo(),这个方法在最终实现比较的时候会有三种返回结构:大于(1)、等于(0)、小于(-1)。

使用 Comparable 接口实现排序功能

class Book implements Comparable<Book> {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    @Override
    public String toString() {
        return "【Book】图书名称:" + title + "、图书价格:" + price + "\n";
    }

    @Override
    public int compareTo(Book o) {
        if (this.price > o.price) {
            return 1;
        } else if (this.price < o.price) {
            return -1;
        } else {
            return 0;
        }
    }
}

public class compareDemo {
    public static void main(String[] args) {
        Book[] books = new Book[] {new Book("Java从入门到项目实战", 99.8),
                new Book("Python从入门到项目实战", 89.7),
                new Book("Go语言从入门到项目实战", 96.3)};

        Arrays.sort(books);
        System.out.println(Arrays.toString(books));
    }
}

输出:
[【Book】图书名称:Python从入门到项目实战、图书价格:89.7
, 【Book】图书名称:Go语言从入门到项目实战、图书价格:96.3
, 【Book】图书名称:Java从入门到项目实战、图书价格:99.8
]

Comparator 比较器

Comparator 比较器是在类定义的时候为类设计的额外功能,但是如果现在有一个类在设计之初并没有考虑到排序的需求,那么这个时候如何可以利用系统类所提供的数组操作形式进行排序?为了解决这样的问题,Java 提供了 Comparator 接口,这个接口可以实现挽救的比较操作,但是需要定义一个专属的比较器实现类。

Arrays 类提供的自定义比较器的排序方法

public static <T> void sort(T[] a, Comparator<? super T> c)

使用 Comparator 实现对象数组排序

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getTitle() {
        return title;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "【Book】图书名称:" + title + "、图书价格:" + price + "\n";
    }

}

// 提供专属比较器实现类
class BookComparator implements Comparator<Book> {

    @Override
    public int compare(Book o1, Book o2) {
        if (o1.getPrice() > o2.getPrice()) {
            return 1;
        } else if (o1.getPrice() < o2.getPrice()) {
            return -1;
        } else {
            return 0;
        }
    }
}

public class compareDemo {
    public static void main(String[] args) {
        Book[] books = new Book[] {new Book("Java从入门到项目实战", 99.8),
                new Book("Python从入门到项目实战", 89.7),
                new Book("Go语言从入门到项目实战", 96.3)};

        Arrays.sort(books, new BookComparator());
        System.out.println(Arrays.toString(books));
    }
}

输出:
[【Book】图书名称:Python从入门到项目实战、图书价格:89.7
, 【Book】图书名称:Go语言从入门到项目实战、图书价格:96.3
, 【Book】图书名称:Java从入门到项目实战、图书价格:99.8
]

Comparator 除了基本的排序支持之外,其内部实际上也存在有大量的数据排序的处理操作,例如 reversed(),如果现在使用的是 Comparable 接口实现这样的反转那么必须进行大量系统源代码的修改,但是如果使用了 Comparator 可以在外部通过具体的方法来进行配置,所以灵活度更高。

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getTitle() {
        return title;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "【Book】图书名称:" + title + "、图书价格:" + price + "\n";
    }

}

// 提供专属比较器实现类
/*class BookComparator implements Comparator<Book> {

    @Override
    public int compare(Book o1, Book o2) {
        if (o1.getPrice() > o2.getPrice()) {
            return 1;
        } else if (o1.getPrice() < o2.getPrice()) {
            return -1;
        } else {
            return 0;
        }
    }
}*/

public class compareDemo {
    public static void main(String[] args) {
        Book[] books = new Book[] {new Book("Java从入门到项目实战", 99.8),
                new Book("Python从入门到项目实战", 89.7),
                new Book("Go语言从入门到项目实战", 96.3)};

        // Comparator支持函数式接口
        Arrays.sort(books, (o1, o2) -> {
            if (o1.getPrice() > o2.getPrice()) {
                return 1;
            } else if (o1.getPrice() < o2.getPrice()) {
                return -1;
            } else {
                return 0;
            }
        });
        System.out.println(Arrays.toString(books));
    }
}

输出:
[【Book】图书名称:Python从入门到项目实战、图书价格:89.7
, 【Book】图书名称:Go语言从入门到项目实战、图书价格:96.3
, 【Book】图书名称:Java从入门到项目实战、图书价格:99.8
]

实现反转操作

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getTitle() {
        return title;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "【Book】图书名称:" + title + "、图书价格:" + price + "\n";
    }

}

// 提供专属比较器实现类
/*class BookComparator implements Comparator<Book> {

    @Override
    public int compare(Book o1, Book o2) {
        if (o1.getPrice() > o2.getPrice()) {
            return 1;
        } else if (o1.getPrice() < o2.getPrice()) {
            return -1;
        } else {
            return 0;
        }
    }
}*/

public class compareDemo {
    public static void main(String[] args) {
        Book[] books = new Book[] {new Book("Java从入门到项目实战", 99.8),
                new Book("Python从入门到项目实战", 89.7),
                new Book("Go语言从入门到项目实战", 96.3)};

        // Comparator支持函数式接口
        Comparator<Book> comparator = (o1, o2) -> {
            if (o1.getPrice() > o2.getPrice()) {
                return 1;
            } else if (o1.getPrice() < o2.getPrice()) {
                return -1;
            } else {
                return 0;
            }
        };
        Arrays.sort(books, comparator.reversed());
        System.out.println(Arrays.toString(books));
    }
}

输出:
[【Book】图书名称:Java从入门到项目实战、图书价格:99.8
, 【Book】图书名称:Go语言从入门到项目实战、图书价格:96.3
, 【Book】图书名称:Python从入门到项目实战、图书价格:89.7
]

总结

两种比较器的区别:

  • Comparable 是在类定义的时候实现的接口,该接口只存在有一个 compareTo() 方法用于确定大小关系;
  • Comparator 是属于挽救的比较器,处理可以实现排序的功能之外,在 JDK 1.8 之后的版本里面还提供有更多方便的数组操作的处理功能。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容