【Java基础教程】(三十九)常用类库篇 · 第九讲:比较器——Comparable和 Comparator的讲解~

在这里插入图片描述

1️⃣ 比较器: Comparable

前面文章中我们已经介绍过了数组以及数组操作工具类Arrays,了解到数组实际上会分为普通数组与对象数组两类使用情况,普通数组可以直接根据数据的大小关系进行排序 (调用 Arrays.sort )排序),而对象数组由于其本身存放的都是地址数据,不可能依据大小关系来实现排序,但是在 Arrays 类中依然重载了一个 sort()方法(对象数组排序: public static void sort(Object[] a)),此方法可以直接针对对象数组实现排序。

而要想使用 sort()方法进行排序,就必须有一个前提:对象所在的类一定要实现 Comparable 接口,否则代码执行时会出现 ClassCastException 异常。而 Comparable 接口就属于比较器的一种,此接口定义如下。

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

Comparable 接口中只定义了一个 compareTo() 方法,此方法返回一个 int 型数据,而开发者覆写此方法时只需要返回3种结果:1(>0)-1(<0)0(=0)

在讲解 String 类的操作方法时曾经讲解过 compareTo()方法,实际上String类、 Integer 等类都实现了Comparable接口,也就是说这些类的对象数组都可以直接利用 Arrays.sort() 方法进行对象数组排序。

//	范例 1: 实现对象数组排序
package com.xiaoshan.demo;

import java.util.Arrays;

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 "书名:"+this.title+",价格:"+this.price+"\n";
	}

	@Override
	public int compareTo(Book o){
    
    	// Arrays.sort()会自动调用此方法比较
		if (this.price > o.price){
    
    
			return 1;
		}else if (this.price < o.price){
    
    
			return -1;
		}else{
    
    
			return 0;
		}
	}
}

public class TestDemo{
    
    
	public static void main(String[] args) throws Exception{
    
    
		Book books []= new Book []{
    
    
			new Book("Java开发实战经典",79.8),
			new Book("JavaWEB开发实战经典",69.8),
			new Book("Oracle开发实战经典",99.8),
			new Book("Android开发实战经典",89.8)
		};
		Arrays.sort(books);                                                    //对象数组排序
		System.out.println(Arrays.toString(books));
	}
}

程序执行结果:

[书名:JavaWEB 开发实战经典,价格:69.8
,书名: Java开发实战经典,价格:79.8
,书名:Android 开发实战经典,价格:89.8
,书名: Oracle开发实战经典,价格:99.8]

此程序在 Book 类定义时实现了 Comparable 接口,这样就意味着此类的对象可以实现对象数组的排序操作。在主类中使用 Arrays.sort()方法时,会自动调用被 Book 类覆写的 compareTo() 方法判断对象的大小关系,这样就会按照价格由低到高进行排序。

在Java中比较器有两种实现方式,其中 Comparable是最为常用的比较器接口。在实际开发中,只要是对象数组排序, 一定要优先考虑使用 Comparable接口实现。

2️⃣ 挽救的比较器: Comparator

利用 Comparable 接口实现的比较器属于常见的用法,但是从另外一个角度来讲,如果要使用 Comparable 比较器,就意味着在类定义时必须考虑好排序的需求。但是如果某一个类定义时并没有实现 Comparable 接口,可是在不能修改类定义时又需要进行对象数组排序该怎么办呢? 为此,Java 又提供了另外一种比较器:Comparator 接口(挽救的比较器),此接口定义如下。

@FunctionalInterface
public interface Comparator<T>{
    
    
	public int compare(T o1, T o2);
	public boolean equals(Object obj);
}

通过定义可以发现在 Comparator 接口上使用了 “@Functionallnterface”注解声明,所以此接口为一个函数式接口。该接口提供了一个 compare() 方法,此方法的返回3种结果为:1(>0)-1(<0)0

有朋友可能有疑问,在定义的接口上使用了"@Functionallnterface" 注解声明, 那么接口中应该只能存在一个抽象方法,为什么此处可以定义两个呢?

需要注意的是,虽然 Comparator 接口中定义了两个抽象方法,但是子类真正在覆写时只需要覆写 compare()方法即可,而 equals() 这样的方法在 Object类中已经有默认实现 (地址比较),也就是说 Object类中的方法是不属于限定范围的。

如果要利用 Comparator 接口实现对象数组的排序操作,还需要更换 java.util.Arrays 类中的排序方 法。对象数组排序方法为:

public static<T> void sort(T[] a, Comparator<? super T> c);
//	范例 2: 利用Comparator 接口实现对象数组排序—— 定义一个类,此类不实现比较器接口
package com.xiaoshan.demo;

class Book {
    
    
	private String title;
	private double price;
	
	public Book(){
    
    }
	public Book(String title, double price){
    
    
		this.title = title;
		this.price = price;
	}
	
	@Override
	public String toString(){
    
    
		return "书名:" + this.title + ",价格:" + this.price + "\n";
	}
	public void setPrice(double price){
    
    
		this.price = price;
	}
	public void setTitle(String title){
    
    
		this.title = title;
	}
	public double getPrice(){
    
    
		return price;
	}
	public String getTitle(){
    
    
		return title;
	}
}

假设在 Book 类的初期设计中并没有排序的设计要求,并且不能够修改。但是随着开发的深入,有了对象数组排序的要求,所以此时就可以利用 Comparator 接口单独为 Book 类设计一个排序的规则类: BookComparator

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;
		}
	}
}

此程序定义了一个 BookComparator 比较器程序类,这样在使用 Arrays.sort() 排序时就需要传递此类的实例化对象。

下面测试使用指定的比较器,实现对象数组的排序操作。

public class TestDemo  {
    
    
	public static void main(String[] args) throws Exception {
    
    
		Book[] books = new Book [] {
    
    
			new Book("Java开发实战经典",79.8),
			new Book("JavaWEB开发实战经典",69.8),
			new Book("'Oracle开发实战经典",99.8),
			new Book("Android开发实战经典",89.8)
		};
		Arrays.sort(books, new BookComparator());
		System.out.println(Arrays.toString(books));
	}
}

程序执行结果:

[ 书名:JavaWEB 开发实战经典,价格:69.8,
书名:Java开发实战经典,价格:79.8,
书名:Android开发实战经典,价格:89.8,
书名:Oracle开发实战经典,价格:99.8 ]

使用 Comparator 并不像 Comparable 那样方便,所以在利用 Arrays 类实现对象排序时,必须明确设置一个排序规则类的实例化对象后才可以正常完成对象数组的排序功能。

3️⃣ Comparable和 Comparator的区别

  • java.lang.Comparable 接口是在要比较的对象类中实现的,用于定义对象之间的自然顺序。它有一个compareTo()方法,用于比较当前对象与参数对象的顺序。实现了Comparable接口的类可以直接使用排序算法(如Arrays.sort()Collections.sort())进行排序。
  • java.util.Comparator 接口是独立于被比较的对象类的。它用于创建一个单独的比较器对象,并实现了compare()方法来定义不同对象之间的比较逻辑。通过提供自定义的Comparator对象,可以对任意对象进行排序,区别于对象自身实现Comparable接口。Comparator接口通常在需要动态改变排序规则或比较非本地类的对象时使用。


温习回顾上一篇(点击跳转)
《【Java基础教程】(三十八)常用类库篇 · 第八讲:数组操作类——解析Arrays类中的全部操作方法,解锁Java数组操作技巧~》

继续阅读下一篇(点击跳转)
《【Java基础教程】(四十)常用类库篇 · 第十讲:反射机制——概念及优缺点、使用方式及底层原理 ~》

猜你喜欢

转载自blog.csdn.net/LVSONGTAO1225/article/details/131848594
今日推荐