Effective Java -- 排序敏感的类建议实现Comparable接口

本文分享了实现Comparable接口对排序类的重要性,讲解了compareTo方法的工作原理,推荐使用装箱类型自带的compare方法,并介绍了Comparator接口在复杂逻辑下的优势。通过PhoneNum实例演示了如何提升代码质量。

此系列文章为本人对《Effective Java》一书的学习笔记,主要是记录对书中重点内容的理解。
既然有缘看到此文,那么希望能对你有所帮助。
本文对应原书 第14条 考虑实现Comparable接口

原因

对于一个有排序需求的类,建议实现Comparable接口,因为这样可以让它轻松地被分类、搜索以及用在基于比较的集合中。

陈述

首先了解一下Comparable:它是一个带泛型的接口,且只有一个方法compareTo;

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

当你实现了这个接口,重写了compareTo方法,这个类便可以很方便地利用Java自带的方法进行排序操作,如Arrays.sort方法。

Java中的所有值类枚举类都实现了这个接口,值类实现Comparable接口的原因很简单,毕竟整天都在忙着比大小,作排序。枚举类的这个实现相对冷门,我们来一起看一下:
打开java.lang.Enum,找到compareTo方法
Enum类的comareTo方法
这里我们可以看到,首先是把两个枚举的class进行了对比(这里不深究其对比逻辑),然后直接输出了ordinal之差。

看一下ordinal是什么:
ordinal
说白了就是一个序号,这个序号怎么产生的呢,很简单,就是枚举在定义的时候顺序

举个例子:

public enum Type {
    A, // ordinal = 0
    B, // ordinal = 1
    C, // ordinal = 2
    D; // ordinal = 3
}

所以Type.D.compareTo(Type.A)的返回值就是3 - 0 = 3
虽然看起来有点意思,但是实际上有用吗?类的作者也考虑到了这一点,于是在注释上写了几笔:

The ordinal of this enumeration constant (its position in the enum declaration, where the initial constant is assigned an ordinal of zero). Most programmers will have no use for this field. It is designed for use by sophisticated enum-based data structures, such as java.util.EnumSet and java.util.EnumMap.

简单来说就是,这玩意对绝大多数的码农来说没啥用,这个是设计给复杂的数据结构如EnumSetEnumMap使用的。

规范

在重写compareTo方法时,作者建议我们使用装箱类型自带的compare方法来替代“>”和“<”,如:Double.compareShort.compare,相对来说会更加简洁明朗。

作者提到,这本书的前两版是建议对于整数类型用“>”和“<”比较,对于浮点型用compare方法。由于Java 7版本开始所有的装箱基本类型都有了compare方法,因此从第三版开始建议大家都使用compare

另外书中作了一个引申,建议我们使用Compartor接口,因为可以更方便的进行一些操作,拿之前博客里面提到的PhoneNum举个例子:

public class PhoneNum implements Comparable<PhoneNum>{

	 /*区号*/
    private Short areaCode;

    /*号码*/
    private Integer num;

    /*分机号*/
    private Integer extensionNum;

	...
}

正常实现方式:

public int compareTo(PhoneNum other) {
        int result = Short.compare(areaCode, other.areaCode);
        if (result == 0) {
            result = Integer.compare(num, other.num);
            if (result == 0) {
                result = Integer.compare(extensionNum, other.extensionNum);
            }
        }
        return result;
    }

Compartor实现:

	// 先构造一个静态的 Comparator
    private static final Comparator<PhoneNum> COMPARATOR = Comparator
            .comparingInt((PhoneNum pn) -> pn.areaCode)
            .thenComparingInt(pn -> pn.num)
            .thenComparingInt(pn -> pn.extensionNum);
            
    public int compareTo(PhoneNum other) {
        return COMPARATOR.compare(this, other);
    }

如此,代码的质量肉眼可见提升了。

总结

对于一个有排序需求的类,为了让它可以轻松地进行分类、搜索以和用在基于比较的集合中,建议实现Comparable接口。在重写compareTo时,记得使用装箱基本类型自带的compare方法,对于逻辑较为复杂的逻辑,建议使用Comparator

水平有限,若文章中存在错误,恳请不吝赐教,这对我以及后面的读者都有重要意义

<think> 我们将按照以下步骤实现: 1. 定义Book实现Comparable接口,在compareTo方法中根据price比较大小。 2. 在SortSearchMainClass的main方法中: a. 创建链表(如LinkedList)来存储多个Book对象。 b. 创建一个新的Book对象。 c. 遍历链表,检查新Book对象与链表中每个Book对象的price是否相等。 d. 输出价格相等的书籍信息。 注意:在compareTo方法中,我们通常返回负数、0或正数来表示小于、等于或大于。但根据需求,我们需要检查价格相等,所以可以在遍历链表时直接比较price值,或者利用compareTo方法返回0表示相等。 但是,注意引用[2]中的实现有误,它只在价格相等时返回1,否则返回0,这不符合Comparable接口的规范(应该返回负数、0、正数)。我们将正确实现compareTo方法。 正确实现Comparable接口的compareTo方法: ```java @Override public int compareTo(Book o) { return Double.compare(this.price, o.price); } ``` 这样,当两个Book对象的价格相等时,compareTo返回0。 然后,在main方法中,我们可以遍历链表,对于每个元素,用新书的compareTo方法与之比较,如果返回0则说明价格相等。或者更直接地,我们可以直接比较两个Book对象的price值。 这里我们采用直接比较price的方式,因为更直观。 代码实现如下: </think> # Java实现Book价格比较功能 根据您的需求,我将实现一个包含价格比较功能的Book,并在主中演示如何检查价格相等的图书。以下是完整的代码实现: ```java import java.util.LinkedList; import java.util.List; class Book implements Comparable<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 int compareTo(Book other) { // 按价格比较,返回负数、零或正数 return Double.compare(this.price, other.price); } @Override public String toString() { return String.format("《%s》 - ¥%.2f", title, price); } } public class SortSearchMainClass { public static void main(String[] args) { // 创建图书列表并添加书籍 List<Book> bookList = new LinkedList<>(); bookList.add(new Book("Java编程思想", 128.0)); bookList.add(new Book("算法导论", 99.5)); bookList.add(new Book("设计模式", 128.0)); bookList.add(new Book("深入理解计算机系统", 89.9)); bookList.add(new Book("Effective Java", 128.0)); // 创建新书检查价格匹配 Book newBook = new Book("Java核心技术", 128.0); System.out.println("图书列表:"); System.out.println("================"); for (Book book : bookList) { System.out.println(book); } System.out.println("\n查找与" + newBook + "价格相同的图书:"); System.out.println("======================================"); boolean found = false; for (Book book : bookList) { // 使用compareTo检查价格相等 if (book.compareTo(newBook) == 0) { System.out.println("匹配: " + book); found = true; } } if (!found) { System.out.println("未找到价格相同的图书"); } // 使用流式API查找价格相同的图书(Java 8+) System.out.println("\n使用流式API查找结果:"); System.out.println("======================"); bookList.stream() .filter(book -> book.compareTo(newBook) == 0) .forEach(System.out::println); } } ``` ## 代码说明 ### 1. Book实现 ```java 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 int compareTo(Book other) { // 使用Double.compare正确处理浮点数比较 return Double.compare(this.price, other.price); } @Override public String toString() { return String.format("《%s》 - ¥%.2f", title, price); } } ``` Book实现Comparable接口,在`compareTo()`方法中使用`Double.compare()`比较价格,这种方式可以正确处理浮点数比较的特殊情况(如NaN和-0.0)。 ### 2. SortSearchMainClass主 ```java public class SortSearchMainClass { public static void main(String[] args) { // 创建链表存储Book对象 List<Book> bookList = new LinkedList<>(); bookList.add(new Book("Java编程思想", 128.0)); // ... 添加更多书籍 // 创建要检查的新书 Book newBook = new Book("Java核心技术", 128.0); // 遍历链表查找价格相同的书 for (Book book : bookList) { if (book.compareTo(newBook) == 0) { System.out.println("匹配: " + book); } } // 使用Java 8流式API实现 bookList.stream() .filter(book -> book.compareTo(newBook) == 0) .forEach(System.out::println); } } ``` ## 程序输出 ``` 图书列表: ================ 《Java编程思想》 - ¥128.00 《算法导论》 - ¥99.50 《设计模式》 - ¥128.00 《深入理解计算机系统》 - ¥89.90 《Effective Java- ¥128.00 查找与《Java核心技术》 - ¥128.00价格相同的图书: ====================================== 匹配: 《Java编程思想》 - ¥128.00 匹配: 《设计模式》 - ¥128.00 匹配: 《Effective Java- ¥128.00 使用流式API查找结果: ====================== 《Java编程思想》 - ¥128.00 《设计模式》 - ¥128.00 《Effective Java- ¥128.00 ``` ## 关键点解释 1. **Comparable接口**:通过实现Comparable接口,定义了图书对象之间的比较规则 2. **浮点数比较**:使用`Double.compare()`方法安全比较double值 3. **链表存储**:使用LinkedList存储图书对象,适合频繁插入删除的场景 4. **两种查找方式**: - 传统for循环遍历 - Java 8流式API(更简洁) 5. **toString方法**:重写toString方法提供有意义的输出 这个实现满足了您的要求,演示了如何实现Comparable接口,在链表中存储对象,以及如何查找价格相同的图书。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值