深入探究java中的 xxxable和xxxator

news2024/11/17 12:48:24

前言

相信有一定工作经验的朋友,都见过或者用过xxxable和xxxator ,比如常见的Comparable和Comparator, 还有还有常见并且容易迷糊的Iterable和Iterator, 看这名字,前两个是和比较相关的, 后两个是和迭代相关. 但是命名如此相似的接口, 又有何区别呢?各自的用途又是什么呢? 今天阿亮带大家一起揭开这神秘的面纱.

揭秘

首先我们要明确的是,xxxable和xxxator都是接口, 都是用来描述其子类具有某种能力的. 因为在java中,接口就是用来定义能力的. 接口中的方法,就代表着其实现类有什么能力. 因为实现类必须要实现接口中的所有方法.

从名称上感受

首先,直观的从名称上直观的感受,复习一下英文

–able: 表形容词, 可…的,能…

able单词
所以,今天我们要研究的其中两个主角,

  • Comparable: 可比较的
  • Iterable: 可迭代的

-ator: 表名词,通常由ate结尾的动词而来, 做事的人或物
ator结尾单词
所以,我们今天要研究的另外两个主角

  • Comparator: 比较器,
  • Iterator: 迭代器,

一番咬文嚼字, 从字面上理解:

xxxable 就是具有xxx能力,是形容词,带入java接口,就这么认为: 实现了xxxable接口,代表着具有了xxx能力,可以进行xxx. 着重描述的是: 实现类具有xxx能力.

xxxator 就是xxx器,是名词, 带入java中的接口,就可以这么认为: 实现了xxxator接口,就代表着可以干xx. 着重描述的是: 实现类可以对某个对象进行xxx.

看到这儿,可能还是有点模糊,不用着急,我们从代码,亲自来感受一下,感受完了再回过头来看这段.

从代码带入

上面我们从名称上理解了,下面我们从实操代码入手,直观感受.

Comparable接口

位于java.lang 包下,只有一个抽象方法

public int compareTo(T o);

实现这个方法,必须实现compareTo这个方法,实现类就具有可比较的能力了.比较肯定要区分大小,那这个方法要怎么区分大小呢? 方法的doc注释上解释了

a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

大意就是 返回一个负整数,零或正整数,表示此对象小于,等于或大于指定的对象。

现在我们假设一个场景,我们有一个<书本>对象,有一个序号属性,通过序号的大小,来对书进行比较,一遍排序整理.指定的规则是: 编号小的更大,编号大的更小,

书籍类的声明如下:

public class Book implements Comparable<Book>{

    private int order;
    private String name;

    public Book(int order, String name) {
        this.order = order;
        this.name = name;
    }

    public int getOrder() {
        return order;
    }

    @Override
    public int compareTo(Book o) {
        //  相等
        if (this == o || this.getOrder() == o.getOrder()) return 0;
        // 如果 此类的order大于被比较的order,返回-1,表示此书"更小"
        if (this.order > o.getOrder()) {
            return -1;
        }
        return 1;
    }

    @Override
    public String toString() {
        return "Book{" +
                "order=" + order +
                ", name='" + name + '\'' +
                '}';
    }

怎么使用呢, 最简单的是比较两本书

        // 比较两本书
        Book songs = new Book(1, "诗经");
        Book threeKingdoms = new Book(2, "三国演义");

        int i = songs.compareTo(threeKingdoms);
        // 返回1, 正数, 代表 诗经 "大于" 三国演义
        System.out.println(i);

感觉这样好像没有什么用. 下面展示一个比较可能会在开发中用到的: 对书籍数组进行排序

        // 对书进行排序
        Book[] books = new Book[3];
        Book songs = new Book(1, "诗经");
        Book threeKingdoms = new Book(2, "三国演义");
        Book soulLand = new Book(3, "斗罗大陆");
        books[0] = soulLand;
        books[1] = songs;
        books[2] = threeKingdoms;
        // 排序之前
        System.out.println(Arrays.toString(books));
        // 利用Arrays.sort进行排序 (这个方法是升序)
        Arrays.sort(books);
        // 排序之后
        System.out.println(Arrays.toString(books));

输出结果为:

[Book{order=3, name='斗罗大陆'}, Book{order=1, name='诗经'}, Book{order=2, name='三国演义'}]
[Book{order=3, name='斗罗大陆'}, Book{order=2, name='三国演义'}, Book{order=1, name='诗经'}]

因为我们定义是: 编号越大,书籍 “越小”,所以这个结果是没问题. Arrays.sort方法就是利用Comparable,进行比较然后排序的

如果Book这个类不实现java.lang.Comparable,然后调用Arrays.sort(books);猜猜会怎样?

同理, java集合Stream流中的sorted()方法,也是同样的道理. 此处不展开, 感兴趣的朋友请移步: java stream使用指南-------sorted使用及进阶

另外,我们知道 java.util.TreeSet 这个集合,添加进去的元素自动就排序好了, 比如我 new TreeSet<Integer> ,然后往里面添加几个数字,打印出来就是有序的, 或者new TreeSet<String>.

    @Test
    public void test4(){
        // 数字TreeSet
        TreeSet<Integer> integers = new TreeSet<>();
        integers.add(20);
        integers.add(11);
        integers.add(34);
        integers.add(49);
        System.out.println(integers);

        // 字符串TreeSet
        TreeSet<String> strings = new TreeSet<>();
        strings.add("B");
        strings.add("E");
        strings.add("G");
        strings.add("A");
        System.out.println(strings);
    }

输出:

[11, 20, 34, 49]
[A, B, E, G]

我添加顺序是随意的, 最终打印出来的结果是有序的, 大家有没有想过这个排序规则是怎么定义的能?在哪里定义的呢? 相信大家已经猜到了, 其实就是Integer String已经实现了java.lang.Comparable接口, TreeSet才知道他们的比较规则,然后由此来进行排序. 如果你往TreeSet中添加一个没有实现Comparable接口的元素, 看看会出现什么情况.

当然,TreeSet还有另一种指定规则的方式,我们下面讨论java.util.Comparator的时候再说

Comparator 接口

说完了Comparable,然后我们来说说Comparator

Comparator 位于java.util包下, 可以翻译为比较器, 是一个函数式接口,其中只有一个抽象方法

 int compare(T o1, T o2);

实现这个方法之后, 就可以对两个对象进行比较了. 因为Comparator是比较器,是工具, 所以可以用这个工具来对两个对象进行比较

比较的规则也是类似, 如果compare方法返回了 了一个正数 0 负数 ,则说明 o1 大于 等于 小于 o2

上代码 ,还是以上面的书籍为例, 但是不再实现Comparable接口

书籍类

public class Book {

    private int order;
    private String name;

    public Book(int order, String name) {
        this.order = order;
        this.name = name;
    }

    public int getOrder() {
        return order;
    }


    @Override
    public String toString() {
        return "Book{" +
                "order=" + order +
                ", name='" + name + '\'' +
                '}';
    }

}

然后我们再为书籍建一个比较器, 书籍比较器

public class BookComparator implements Comparator<Book> {   
    
    @Override
    public int compare(Book o1, Book o2) {
        if (o1 == o2 || o1.getOrder() == o2.getOrder()) return 0;
        
        if (o1.getOrder() > o2.getOrder()) {
            return -1;
        }
        return 1;
    }
    
}

我们还是先比较两本书,但是此时Book已经没有实现Comparable接口, 没有compareTo方法,所以不能直接比较,需要使用 书籍比较器 进行比较

        // 比较两本书
        Book songs = new Book(1, "诗经");
        Book threeKingdoms = new Book(2, "三国演义");

        // 使用书籍比较器进行比较
        BookComparator bookComparator = new BookComparator();
        int i = bookComparator.compare(songs, threeKingdoms);
        // 返回1, 正数, 代表 诗经 "大于" 三国演义
        System.out.println(i);

再来排序,

        // 对书进行排序
        Book[] books = new Book[3];
        Book songs = new Book(1, "诗经");
        Book threeKingdoms = new Book(2, "三国演义");
        Book soulLand = new Book(3, "斗罗大陆");
        books[0] = soulLand;
        books[1] = songs;
        books[2] = threeKingdoms;
        // 排序之前
        System.out.println(Arrays.toString(books));
        // 利用Arrays.sort进行排序,因为此时Book没有实现Comparable接口,直接使用下面的方法是没办法进行排序的
        // Arrays.sort(books);
        // 利用 书籍比较器进行排序
        BookComparator bookComparator = new BookComparator();
        Arrays.sort(books,bookComparator);
        // 排序之后
        System.out.println(Arrays.toString(books));

结果:

[Book{order=3, name='斗罗大陆'}, Book{order=1, name='诗经'}, Book{order=2, name='三国演义'}]
[Book{order=3, name='斗罗大陆'}, Book{order=2, name='三国演义'}, Book{order=1, name='诗经'}]

比较器毕竟是工具, 所以工具还提供了一些额外的方法比如reversed

上面的排序, 我看着不太舒服, 我想倒序一下, 当然比较器已经固定了,就不能再改了

在排序时,可以这样

Arrays.sort(books,bookComparator.reversed());

排序后的结果就是:

[Book{order=1, name='诗经'}, Book{order=2, name='三国演义'}, Book{order=3, name='斗罗大陆'}]

同理, java集合Stream流中除了sorted()方法,还提供了一个重载方法 sorted(Comparator<? super T> comparator) 也是同样的道理.传入一个比较器进行比较, 此处不展开, 感兴趣的朋友请移步: java stream使用指南-------sorted使用及进阶

上面在讲TreeSet时,我们还留了一个悬念, TreeSet 可以通过另外一种方式指定排序规则, 那肯定是用 比较器来指定规则啦

首先我们使用前面的方式(注意:此时Book未实现Comparable接口)

        // 我们先来试试之前我用的方法
        TreeSet<Book> books = new TreeSet<>();
        books.add(new Book(1, "诗经"));
        books.add(new Book(3, "斗罗大陆"));
        books.add(new Book(2, "三国演义"));
        System.out.println(books);

肯定会出问题的,因为此时TreeSet已经不知道用什么规则对添加进来的元素排序了,此时应该使用另一种方式

        // new 书籍比较器
        BookComparator bookComparator = new BookComparator();
        // 构造TreeSet时将比较器传入
        TreeSet<Book> books = new TreeSet<>(bookComparator);
        books.add(new Book(1, "诗经"));
        books.add(new Book(3, "斗罗大陆"));
        books.add(new Book(2, "三国演义"));
        System.out.println(books);

结果也是一样正确的 被排序好的.

这里多提一嘴, 因为Comparator是个函数式接口, 更多时候我们使用的匿名内部类或者lambda来实现一个构造器,而不是单独的创建一个书籍比较器, 除非这个比较器需要在不止一个地方使用,此处不展开描述.

好了到此为此, 我们已经搞清楚了Comparable和Comparator的用法

总结

我们已经知道Comparable和Comparator是怎么使用的了,回头再去看 最初的字面描述,肯定有更深的理解.

我现在做个总结: xxxable和xxxator 都是为做xxx事情而存在
xxxable是个形容词, 着重描述的是: 其实现类本身就具有xxx能力,
xxxator是个名词,意思是其实现类本身是xxx器, 一些不具备xxx能力的类可以借助xxx器有用xxx能力

面向对象, 也是对现实世界的抽象, 我来举个例子,以飞行(fly)举例,
Flyable: 可飞行的
Flyator: 飞行器

一般的鸟类(Bird)都应该实现Flyable接口,因为鸟类本身就具有飞行的能力

人类(Human)不能实现Flyable接口,因为人类不具有飞行的能力, 但是应该存在 人类飞行器(HumanFlyator)这个类,这个类实现Flyator接口, 人类本身不具有飞行的能力,但是人类可以借助飞行器飞行.

还有Iterable和Iterator,也是一样的,但是和 <比较> 不同的是, Iterable的迭代能力是借助Iterator完成的, 此处不在展开描述,感兴趣的朋友可以自行研究.

不得不说,人家设计java语言的人,也是对现实世界有充分认识的,这些命名是如此的深刻,内涵. 我们在自己的工作中,也可以参考,进行规范的接口设计

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455695.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详解车载设备FOTA测试

作者 | 李伟 上海控安安全测评部总监 来源 | 鉴源实验室 引言&#xff1a;上一篇文章我们以车载Tbox为例介绍了相关的性能测试&#xff08;车载TBOX嵌入式设备软件的性能测试&#xff09;&#xff0c;本篇我们介绍另外一个重要功能的专项测试&#xff1a;OTA&#xff08;Over…

MySQL安装及卸载

安装 mysql现在安装的是5.7.mysql的安装方式有两种: 一种是exe方式 另外一种解压版 这次就使用解压版安装 解压缩到非中文目录 编写配置文件 1) 在安装目录下新建my.ini的配置文件 打开文件后缀和隐藏文件显示 2) 新建文件内编写内容 [Client] port 3306 [mysqld] #设置330…

【移植Ardupilot的日志记录方法到linux上】

移植Ardupilot的日志记录方法到linux上 说明日志结构组成日志写入操作预定义日志项运行时添加日志项的方法 单例测试编译方法查看数据其他 说明 采用二进制文件记录&#xff0c;可在mission planer查看 支持所有数据类型记录精巧移植方便可直接在地面站绘制曲线查看可导出生成…

觉非科技发布:基于BEV的数据闭环融合智驾解决方案

2023年上海车展期间&#xff0c;觉非科技基于BEV的数据闭环融合智能驾驶解决方案正式发布。 该方案可通过量产车BEV的实时感知结果&#xff0c;提供完整的城市Map-lite及Map-free数据闭环融合解决方案&#xff0c;并满足城市NOA、记忆通勤/泊车以及感知大模型训练的需要。 车…

OSPF基础配置实验

目录 一、实验要求与拓扑结构 1、实验要求 2、提前规划好的网段以及拓扑结构如下图 二、实验步骤 1、给各个路由器的每个接口配ip 2、运行ospf协议并划分区域 一、实验要求与拓扑结构 1、实验要求 首先划分区域&#xff0c;蓝色区域为Area 0&#xff0c;黄色区域为Area…

MyBatis(十五)MyBatis的逆向工程

前言、 所谓的逆向工程是&#xff1a;根据数据库表逆向生成Java的pojo类&#xff0c;SqlMapper.xml文件&#xff0c;以及Mapper接口类等。 要完成这个工作&#xff0c;需要借助别人写好的逆向工程插件。 思考&#xff1a;使用这个插件的话&#xff0c;需要给这个插件配置哪些…

基于 TensorRT 使用 python 进行推理优化

文章大纲 简介TensorRT 简介构建测试的conda 环境注意事项TensorRT 安装参考文献与学习路径简介 TensorRT 简介 TensorRT是NVIDIA推出的一个高性能的深度学习推理框架,可以让深度学习模型在NVIDIA GPU上实现低延迟,高吞吐量的部署。TensorRT支持Caffe,TensorFlow,Mxnet,P…

【Python】实战:生成无关联单选问卷 csv《压疮风险评估表》

目录 一、适用场景 二、业务需求 三、Python 文件 &#xff08;1&#xff09;创建文件 &#xff08;2&#xff09;代码示例 四、csv 文件 一、适用场景 实战场景&#xff1a; 问卷全部为单选题问卷问题全部为必填问题之间无关联关系每个问题的答案分数不同根据问卷全部问…

FPGA基于XDMA实现PCIE X8采集AD7606数据 提供工程源码和QT上位机程序和技术支持

1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PCI 以及更早期的计算机总线的共享并行架构&#xff0c;每个设备都有自己的专用连接&#xff0c;不需要向整个总线请求带宽&#xff0c;而且可以把数据传输率提高到一个很…

倍数+路径之谜

倍数 :用户登录https://www.lanqiao.cn/problems/583/learning/?page5&first_category_id1&sortstudents_count 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 请问在 1 到 2020 中&#xff0c;有多少个…

Java十大排序算法

排序算法 对一序列对象根据某个关键字进行排序 &#xff08;1&#xff09;稳定性 在排序中对于相等的两个元素a、b。如果排序前a在b的前边&#xff0c;排序之后a也总是在b的前边。位置不会因为排序而改变称之为稳定。反之&#xff0c;如果排序后a、b的位置可能会发生改变&am…

Java线程池从入门到精通(线程池实战)

参考 java常用线程池及它们的适用场景&#xff08;JDK1.8&#xff09; Java线程与线程池实战 线程池的拒绝策略_线程池 RejectedExecutionHandler 拒绝策略 ThreadPoolExecutor原理解析-关闭线程池 代码经验—java获取cpu个数-docker 一、概念 Java 中的线程池核心实现类是 …

鉴源论坛 · 观模丨面向界面的图形化测试技术

作者 | 熊一衡 华东师范大学软件工程学院博士 苏亭 华东师范大学软件工程学院教授 版块 | 鉴源论坛 观模 01 什么是面向界面的图形化测试&#xff08;GUI Testing&#xff09; 图形用户界面(GUI) 是一种通过图形化方式呈现信息、数据、功能和操作的用户界面&#xff0c;旨在…

一起学 WebGL:三角形加上渐变色

大家好&#xff0c;我是前端西瓜哥。之前教大家绘制一个红色的三角形&#xff0c;这次我们来画个有渐变的三角形。 本文为系列文章&#xff0c;请先阅读如何绘制红色三角形的文章&#xff1a; 《一起学 WebGL&#xff1a;绘制三角形》 原来的写法&#xff0c;颜色是在片元着色器…

移动边缘计算意味着真正的5G时代已经来临

5G的承诺尚未实现&#xff0c;但现在宣布其失败还为时过早。DataBank首席执行官劳尔k马丁尼克(Raul K. Martynek)表示 &#xff0c;真正的5G正在通过移动边缘计算实现&#xff0c;而数据中心将成为其中的核心。 在美国所有主要的移动运营商都在大力宣传他们在全美范围内提供无…

STM32-移植RTT

目录 Cubemx引入RTT资源新建工程生成工程 时钟选择选单片机引脚引脚搜索快速选中取消引脚选中引脚命名IO普通模式设置 串口串口基本配置串口DMA ADC采集ADC基本应用ADC_DMA RTT-shell指令定义RTTCOM调试串口J-Link RTT调试 教程shell指令RTT外设驱动使用1--串口添加 STM32_pwm …

玩元宇宙血亏后 蓝色光标梭哈AI也挺悬

蓝色光标2022年年度报告出炉&#xff0c;巨亏21.75 亿元&#xff0c;其中20.38亿亏损因商誉、无形资产及其他资产减值造成&#xff0c;而在实际亏损业务中&#xff0c;元宇宙占比不小。 蓝色光标在元宇宙领域的布局&#xff0c;主要通过三家子公司实施&#xff0c;分别为蓝色宇…

分布式文件系统HDFS的多问多答

分布式文件系统HDFS 简述HDFS的优缺点简述HDFS的体系结构请论述HDFS中SecondaryNameNode的作用和工作原理请论述HDFS写数据原理 简述HDFS的优缺点 HDFS的优良特性&#xff1a; ①兼容廉价的硬件设备。在成百上千台廉价服务器中存储数据&#xff0c;常会出现节点失效的情况&…

从浏览器输入url到页面加载(四)协议栈和套接字以及三次握手确认对于通信的作用

前言 上一节我们说到了域名对用户记忆的优点&#xff0c;但是IP对于路由器的优点&#xff0c;所以需要有DNS服务器提供域名与IP地址的转换&#xff0c;还说到了在前端开发中dns-prefetch域名预解析的好处。 本小节呢&#xff0c;我们会说一些不常用的知识点&#xff0c;如协议…

【社区图书馆】读《悲惨世界》有感

文章目录 故事简介经典重现价值取向我的思想 故事简介 《悲惨世界》是一部充满了悲剧的小说&#xff0c;故事首先由教堂展开&#xff0c;然后主要围绕着主人公冉阿让进行一系列的生动形象的描写&#xff0c;讲述了冉阿让悲惨的一生。 主人公冉阿让是一个诚实、善良的工人&…