【Effective Java 14】考虑实现 Comparable 接口
2022/4/14 11:16:15
本文主要是介绍【Effective Java 14】考虑实现 Comparable 接口,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1. 什么时候应该让类实现 Comparable 接口
Comparable
接口是一个泛型接口,代码如下:
public interface Comparable<T> { int compareTo(T t); }
类实现 Comparable
接口,就表明它的实例具有内在的排序关系,比如按照字母顺序、按数值顺序或者按年代顺序,那你就应该考虑实现 Comparable
接口:
一般来说,值类适合实现 Comparable
接口。
2. 使用 Comparable 接口的约定
将一个对象与另一个对象进行比较时。当对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或正整数。如果由于指定对象的类型而无法与该对象进行比较,则抛出 ClassCastException
异常。
实现 Comparable 接口的类,在实现时应该满足以下约定(注意,在下面的说明中,符号 sgn(...) 表示数学中的 signum 函数,它根据表达式的值为负、零、正,分别返回-1,0,1):
- 实现者必须确保所有的 x 和 y 都满足
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
(交换律) - 实现者必须确保比较关系可以传递:
(x.compareTo(y) > 0 && y.compareTo(z) > 0)
则x.comareTo(z) > 0
;(传递律) - 实现着必须确保
x.compareTo(y) == 0
则对于所有的z
都满足sgn(x.compareTo(z)) == sgn(y.compareTo(z))
- 强烈建议对于值类,满足
(x.compareTo(y) == 0) == (x.equals(y))
。但也并非绝对必要,如果违反这一条件,都应该在文档中明确予以说明。
注意,Comparable
接口不能跨越不同类型进行比较,在比较不同类型的对象时,可以在 compareTo
函数中抛出 ClassCastException
异常来拒绝执行。
违反 Comparable
接口约定会破坏其他依赖于比较关系的类或方法,如 TreeSet
、TreeMap
、工具类 Collections
,Arrays
,因为它们内部都用与排序相关的算法。
3. 使用 Comparable 接口的建议
- 在
compareTo
方法中使用关系操作符>
或<
去比较基本类型是十分繁琐的,或者使用-
加>0
判断。对于基本类型在compareTo
函 数中进行比较应该使用静态方法代替,如Integer.compare(a, b)
、Float.compare(a, b)
、Double.compare(a, b)
等等。
// 太繁琐 public int compare(Integer A, Integer B) { if A > B { return 1; } else if A < B { return -1; } else { return 0; } } // 有溢出风险 public int compare(Integer A, Integer B) { return A - B; } // 最佳 public int compare(Integer A, Integer B) { return Integer.compare(A, B); }
- 如果一个类有多个关键域,按照什么样的顺序来比较这些域是十分关键的。你必须从最关键的域开始,逐步进行到所有重要的域。
4. 使用 Comparator 接口
Comparator
接口配置了一组比较构造器方法,使得比较器的构造工作变得更加流畅。之后,按照Comparable
接口的要求,这些比较器可以用来实现一个compareTo
方法。如下:
private static class PhoneNumber implements Comparable<PhoneNumber>{ private final short areaCode; private final short prefix; private final short lineNum; public PhoneNumber(short areaCode, short prefix, short lineNum) { this.areaCode = areaCode; this.prefix = prefix; this.lineNum = lineNum; } // 使用 Comparator 可以轻松实现对关键域按优先级进行比较 private static final Comparator<PhoneNumber> COMPARATOR = Comparator .comparingInt((ToIntFunction<PhoneNumber>) value -> value.areaCode) .thenComparingInt(value -> value.prefix) .thenComparingInt(value -> value.lineNum); @Override public int compareTo(PhoneNumber o) { return COMPARATOR.compare(this, o); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PhoneNumber that = (PhoneNumber) o; return areaCode == that.areaCode && prefix == that.prefix && lineNum == that.lineNum; } @Override public int hashCode() { return Objects.hash(areaCode, prefix, lineNum); } }
5. 总结
- 对于排序敏感类,都应该让该类实现 Comparable 接口
- 在
compareTo
函数中避免使用 > 或 < 操作符,不要通过减法运算去进行判断,有可能溢出 - 如果
compareTo
函数编写起来比较复杂,比如关键域较多的情况,应该单独实现一个静态Comparator
比较构造器,并通过它简介实现comparaTo
接口。
这篇关于【Effective Java 14】考虑实现 Comparable 接口的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)