Comparable
为什么会有Comparable与Comparator接口? 引入策略模式
引入
- 大家先考虑一个场景, 有一个整形数组, 我们希望通过调用一个工具类的排序方法就能对该数组进行排序. 请看下面的代码:
public class Strategy {
public static void main(String[] args) {
int[] arr = {5, 3, 1, 7, 2};
new DataSorter().sort(arr);//调用工具类进行排序
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
class DataSorter{//用于排序的工具类
public void sort(int[] arr){//调用sort方法进行排序, 此处使用冒泡排序
for(int i = arr.length - 1; i > 0; i--){
for(int j = 0; j < i; j++){
if(arr[j] > arr[j + 1])
swap(arr, j, j + 1);
}
}
}
private void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
Comparable接口的来龙去脉
- 通过上面的代码, 我们能够轻易地对整形数组进行排序, 那么如果现在有了新需求, 需要对浮点类型数据进行排序, 排序工具类应该如何做呢?
- 或许你会想, 不如就新添加一个排序方法, 方法的参数类型为
float
类型, 把int类型数组的排序算法复制一遍不就可以了吗? - 那如果我继续追问, 如果现在要对一只猫进行排序, 那应该怎么做呢? 猫的类如下
class Cat{
private int age;//猫的年龄
private int weight;//猫的体重
//get / set 方法...
}
- 你也许会顺着原来的思路回答, 照样copy一份排序的算法, 修改方法参数, 然后在比较的地方指定比较猫的年龄或体重不就可以了吗?
public void sort(Cat[] arr){//以猫数组作为参数
for(int i = arr.length - 1; i > 0; i--){
for(int j = 0; j < i; j++){
if(arr[j].getAge() > arr[j + 1].getAge())//根据猫的年龄作比较
swap(arr, j, j + 1);
}
}
}
- 但仔细想想, 如果还要继续比较小狗, 小鸡, 小鸭等各种对象, 那么这个排序工具类的代码量岂不是变得很大? 为了能让排序算法的可重用性高一点, 我们希望排序工具中的
sort()
方法可以对任何调用它的对象进行排序. - 你可能会想: 到对任何对象都能排序, 把
sort()
方法的参数改为Object
类型不久可以了嘛. 这个方向是对的, 但是问题是, 当拿到两个Object
类型对象, 应该根据什么规则进行比较呢? - 这个时候我们自然而然地就希望调用工具类进行排序的对象本身就具备自己的
比较法则
, 这样在排序的时候就能直接调用对象的排序法则进行排序了. - 我们把比较法则抽象为
Comparable
接口, 凡是要进行比较的类都要实现Comparable
接口, 并且定义自己的比较法则, 也就是CompareTo()
方法. - 这样当我们在封装工具时, 就可以直接对实现了
Comparable
接口的对象进行比较, 不用担心比较的细节了.
public class Strategy {
public class Strategy {
public static void main(String[] args) {
// Integer[] arr = {5, 3, 1, 7, 2};//注意这里把int改为Integer, Integer是Object的子类
Cat[] arr = {new Cat(3, 3), new Cat(1, 1), new Cat(5, 5)};
DataSorter ds = new DataSorter();
ds.sort(arr);
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
}
class DataSorter{//用于排序的工具类
public void sort(Object[] arr){//参数类型为Object
for(int i = arr.length - 1; i > 0; i--){
for(int j = 0; j < i; j++){
Comparable c1 = (Comparable) arr[j];//先转为Comparable类型
Comparable c2 = (Comparable) arr[j + 1];
if(c1.CompareTo(c2) == 1)//调用CompareTo()进行比较, 不关心具体的实现
swap(arr, j, j + 1);
}
}
}
private void swap(Object[] arr, int i, int j){
Object temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
class Cat implements Comparable{
private int age;
private int weight;
@Override
public int CompareTo(Object o) {
if(o instanceof Cat){//先判断传入的是否是Cat类对象, 不是则抛异常
Cat c = (Cat) o;
if(this.age > c.age) return 1;
else if (this.age < c.age) return -1;
else return 0;
}
throw null == o ? new NullPointerException() : new ClassCastException();
}
// get / set ...
//toString() ...
}
interface Comparable{
public int CompareTo(Object o);
}
引入Comparator接口
- 相信看了上面的
Comparable
接口来由, 大家会感觉整个设计又美好了一些, 但是其中还有漏洞. 我们在Cat
类的CompareTo()
方法中, 对猫的比较策略是写死的, 现在我们按猫的年龄比较大小, 如果哪天我们想按照猫的体重比较大小, 又要去修改源码了. 有没有扩展性更好的设计? - 我们可以让用户自己定义一个比较器类, 对象可以根据用户指定的比较器比较大小.
- 整个逻辑是: 如果这个对象需要进行比较, 那么它必须实现
Comparable
接口, 但是它具体是怎么比较的, 则通过具体的Comparator
比较器进行比较. - 当然这里少不了多态, 我们首先要定义一个比较器接口
Comparator
, 用户的比较器需要实现Comparator
接口, 下面上代码:
public class Strategy {
public static void main(String[] args) {
Cat[] arr = {new Cat(3, 3), new Cat(1, 1), new Cat(5, 5)};
DataSorter ds = new DataSorter();
ds.sort(arr);
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
class DataSorter{//用于排序的工具类
public void sort(Object[] arr){//参数类型为Object
for(int i = arr.length - 1; i > 0; i--){
for(int j = 0; j < i; j++){
Comparable c1 = (Comparable) arr[j];//先转为Comparable类型
Comparable c2 = (Comparable) arr[j + 1];
if(c1.CompareTo(c2) == 1)//背后已经转换为使用Comparator的定义的规则进行比较
swap(arr, j, j + 1);
}
}
}
private void swap(Object[] arr, int i, int j){
Object temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
class Cat implements Comparable{
private int age;
private int weight;
private Comparator comparator = new CatAgeComparator();//默认持有年龄比较器
@Override
public int CompareTo(Object o) {
return comparator.Compare(this, o);//调用比较器比较而不是直接在此写比较法则
}
// get / set / toString ...
}
interface Comparable{
public int CompareTo(Object o);
}
interface Comparator{
public int Compare(Object o1, Object o2);
}
//用户自己定义的, 按照猫的年龄比较大小的比较器
class CatAgeComparator implements Comparator{
@Override
public int Compare(Object o1, Object o2) {
Cat c1 = (Cat) o1;
Cat c2 = (Cat) o2;
if(c1.getAge() > c2.getAge()) return 1;
else if(c1.getAge() < c2.getAge()) return -1;
else return 0;
}
}
//按照猫的体重比较大小的比较器
class CatWeightComparator implements Comparator{
@Override
public int Compare(Object o1, Object o2) {
Cat c1 = (Cat) o1;
Cat c2 = (Cat) o2;
if(c1.getWeight() > c2.getWeight()) return 1;
else if(c1.getWeight() < c2.getWeight()) return -1;
else return 0;
}
}
什么是策略模式?
- 在上面的例子中, 我们自己定义了
Comparable
接口和Comparator
接口, 其实这两个接口都是Java自带的, 通过上面的代码示例, 想必大家也应该知道了为什么会有这两个接口. - 其实
Comparable
定义的就是一种比较的策略, 这里的策略你可以理解为一个功能, 然而策略有了, 我们还需要有具体的策略实现, 于是便有了Comparator
接口.
- 这里再举一个例子方便大家理解.
- 现在有一个坦克小游戏, 坦克要能够发射炮弹, 那么我们可以认为发射炮弹就是一种策略, 但是具体到发送什么炮弹, 这可以由具体的策略实现.
- 到GitHub上看看该坦克游戏
- 首先定义发射炮弹这种策略
public interface Fire {
public void fire();//发射炮弹的策略
}
- 为了实现发射炮弹这种策略, 定义策略的具体实现, 也就是定义发射炮弹动作
public interface FireAction {
public void fireAction(Tank tank);
}
- 坦克想要发送炮弹必须实现
Fire()
接口, 而且坦克拥有发射炮弹的动作, 至于动作的具体实现, 这里默认给出只发射一颗炮弹的动作.
public class Tank implements TankHitListener, Fire {
//省略各种属性方法...
private FireAction fireAction = new NormalFireAction();//默认动作是只发射一颗炮弹
@Override
public void fire() {
fireAction.fireAction(this);
}
//...
使用了策略模式有什么好处?
- 以上面的坦克游戏为例, 当把发射炮弹定义为一种策略后, 能发射炮弹的对象就不只坦克一个了, 如果游戏中有机关, 可以让机关也实现
fire()
接口, 获得发射炮弹的能力. - 而且在定义策略后我们可以根据策略给出不同的实现方式, 比方说坦克发射炮弹的动作是每次只发射一颗炮弹, 而机关是每次向八个方向发射一颗炮弹. 非常灵活.
- 结束
Comparable的更多相关文章
- Java中Comparable与Comparator的区别
相同 Comparable和Comparator都是用来实现对象的比较.排序 要想对象比较.排序,都需要实现Comparable或Comparator接口 Comparable和Comparator都 ...
- 12.Java中Comparable接口,Readable接口和Iterable接口
1.Comparable接口 说明:可比较(可排序的) 例子:按照MyClass的y属性进行生序排序 class MyClass implements Comparable<MyClass> ...
- 泛型的排序问题(Collections.sort及Comparable的应用)
一.前言 java中对泛型(集合类型)排序的问题,主要采用了两张方式一种是对要排序的实体类,实现Comparable接口,另一种方式,Collections集合工具类进行排序. 二.实现Comp ...
- 【集合框架】JDK1.8源码分析之Comparable && Comparator(九)
一.前言 在Java集合框架里面,各种集合的操作很大程度上都离不开Comparable和Comparator,虽然它们与集合没有显示的关系,但是它们只有在集合里面的时候才能发挥最大的威力.下面是开始我 ...
- 对象比较器:Comparable和Comparator
在进行对象数组排序的过程中需要使用到比较器,比较器有两个:Comparable和Comparator ①.java.lang.Comparable:是在类定义是时候默认实现好的接口,里面提供有一个co ...
- java中的Comparable接口
类对象之间比较"大小"往往是很有用的操作,比如让对象数组排序时,就需要依赖比较操作.对于不同的类有不同的语义.如Student类,比较2个学生对象可以比较他们的score分数来评判 ...
- java://Comparator、Comparable的用法(按照要求将map集合的键值对进行顺序输出)
import java.util.*; public class Person implements Comparable<Person>//使Person的属性具有比较性 { priva ...
- Java中Comparable和Comparator接口区别分析
Java中Comparable和Comparator接口区别分析 来源:码农网 | 时间:2015-03-16 10:25:20 | 阅读数:8902 [导读] 本文要来详细分析一下Java中Comp ...
- Comparable和Comparator的区别
Comparable Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较 ...
- java.lang.Comparable<T> 接口
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T ...
随机推荐
- Django | pycharm 提示 unresolved attribute referene 'objects' for class 'xxxx'
objects高亮,提示信息为unresolved attribute referene 'objects' for class 'BookInfo' 当前情况是pycharm没有识别到objects ...
- 面试官所问的--Token认证
写这一篇文章的来源是因为某一天的我被面试官提问:让你设计一个登录页面,你会如何设计? 我当时的脑子只有??? 不就是提交账号.密码给后台就搞定了呢? 不可能那么简单,我弱弱的想,难道要对密码加密?? ...
- 可持久化0-1 Trie 简介
Trie树是字符串问题中应用极为广泛的一种数据结构,可以拓展出AC自动机.后缀字典树等实用数据结构. 然而在此我们考虑0-1 Trie的应用,即在序列最大异或问题中的应用. 这里的异或是指按位异或.按 ...
- SigXplorer设置延时及Local_Global
通过SigXplorer设置绝对延时和相对延时及对Local-Global的理解 一.基本理解 (感觉可能有偏差) 在于博士的教程第44和45讲中,分别对绝对延时和相对延时进行了设置,通过SigXpl ...
- ORA-00917: missing comma
问题描述 ORA-00917: missing comma 问题原因 逗号,引号什么的多了或者少了,或者换行引起的
- python3练习100题——003
今天继续-答案都会通过python3测试- 原题链接:http://www.runoob.com/python/python-exercise-example3.html 题目:一个整数,它加上100 ...
- 结合字符串常量池/String.intern()/String Table来谈一下你对java中String的理解
1.字符串常量池 每创建一个字符串常量,JVM会首先检查字符串常量池,如果字符串已经在常量池中存在,那么就返回常量池中的实例引用.如果字符串不在池中,就会实例化一个字符串放到字符串池中.常量池提高了J ...
- Uncaught TypeError: Cannot read property 'addEventListener' of null
<script type="text/javascript"> var body1=document.getElementById('#body') </scri ...
- java_jsp_导入第三方jar包
问题:把第三方jar包放在tomcat common/lib目录下之后,在jsp页面中引用不到 解决方法:将jar包放在你的项目目录下的WEB-INF/lib/目录下 解决 希望对大家又所帮助 以上
- bootstrap联动校验(转载)
接触bootstrapvalidator时间不久,最近需要多个字段共同验证,网上查了一下未找到,查阅api文档,发现确实可以实现. 先看dom <div class="form-gro ...