欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

java中的xxxable和xxxator使用及说明,

来源: javaer 分享于  点击 17279 次 点评:182

java中的xxxable和xxxator使用及说明,


目录
  • 前言
  • 揭秘
    • 从名称上感受
    • 从代码带入
      • Comparable接口
      • Comparator 接口
  • 总结

    前言

    相信有一定工作经验的朋友,都见过或者用过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语言的人,也是对现实世界有充分认识的,这些命名是如此的深刻,内涵. 我们在自己的工作中,也可以参考,进行规范的接口设计

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持3672js教程。

    您可能感兴趣的文章:
    • Java并发Futures和Callables类实例详解
    • 使用Java动态创建Flowable会签模型的示例代码
    • Java中callable的实现原理
    • java.sql.SQLRecoverableException关闭的连接异常问题及解决办法
    • Java中的接口以及常见的Cloneable接口用法
    相关栏目:

    用户点评