专栏首页Java小白成长之路第3次文章:自定义类排序

第3次文章:自定义类排序

对自定义类的排序方法:

在现实生活中,我们需要对很多信息进行相应的排序,然后呈现给大家查看,有些数据是可以直接排序的,比如说我们最常见的数字,可以按照升序或者降序的方法来进行排列,又比如说日期,可以按照时间的远近来进行排序。这些都是最为常见的信息排序。

但是生活中会有许多信息并不能通过这种简单的方法来进行处理。举个例子:新闻信息,它的排序规则可能是这样的:先按照发布时间进行排列,把最新的新闻放在首页,再按照点击率进行排列,再按照标题进行排列,等等排列因素放在一起,形成我们最后看到的消息顺序。所以我们在做相应的信息处理时,我们需要想办法来解决这些消息的排序问题。再或者说当我们打开淘宝网站时,呈现给我们的商品可能是按照多种排序规则最后呈现出来的结果。比如先按照商品的收藏量进行降序排列,再按照相关度进行降序排列,再按照价格等诸多因素进行排序(这些排序只是个人的猜测,不代表真实情况啊,别被我误导了,实际情况会比这些复杂)。这些现实中的实体类的排序规则就需要考虑到更多的规则来进行操作。这周学习到了两种方法,对我们的自定义类进行排序。如下所示:

方法1、在实体类中实现java.lang.comparable接口,然后重写相应的compareTo方法,下面结合一个实例来进行解释:

1)首先我们自己定义一个新闻类,主要属性就是标题、发布时间、以及点击率

package com.peng01.sort.refType;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 新闻条目的实体类
 * 使用Comparable接口
 */
public class NewsItem implements java.lang.Comparable<NewsItem>{//实现comparable接口
    private String title;//新闻标题
    private Date pubTime;//新闻发布的时间
    private int hits;//新闻的点击率
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Date getPubTime() {
        return pubTime;
    }
    public void setPubTime(Date pubTime) {
        this.pubTime = pubTime;
    }
    public int getHits() {
        return hits;
    }
    public void setHits(int hits) {
        this.hits = hits;
    }
    public NewsItem(String title, Date pubTime, int hits) {
        super();
        this.title = title;
        this.pubTime = pubTime;
        this.hits = hits;
    }
    public NewsItem() {
    }
    // 按照发布时间降序+点击率降序+新闻标题升序
    @Override
    public int compareTo(NewsItem o) {//重写comparable接口中的compare方法
        int result;
        result = -this.pubTime.compareTo(o.pubTime);//默认的是升序,所以加一个负号成为降序
        if(0 == result) {//时间相同
            //然后看点击量
            result = -(this.hits-o.hits);//点击率为降序
            if(0 == result) {//点击量相同
                //然后看标题
                result = this.title.compareTo(o.title);//标题按照升序            
            }
        }
        return result;
    }
    @Override
    public String toString() {//重新toString方法
        StringBuilder sb = new StringBuilder();
        sb.append("标题:").append(this.title);
        sb.append(",时间:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.pubTime));
        sb.append(",点击率:").append(this.hits).append("\n");
        return sb.toString();
    }
}

我将整个代码都放上去了,为了方便大家看清楚一点,我把核心代码单独拿出来给大家分析:

// 按照发布时间降序+点击率降序+新闻标题升序
    @Override
    public int compareTo(NewsItem o) {//重写comparable接口中的compare方法
        int result;
        result = -this.pubTime.compareTo(o.pubTime);//默认的是升序,所以加一个负号成为降序
        if(0 == result) {//时间相同
            //然后看点击量
            result = -(this.hits-o.hits);//点击率为降序
            if(0 == result) {//点击量相同
                //然后看标题
                result = this.title.compareTo(o.title);//标题按照升序            
            }
        }
        return result;
    }

这段代码就属于使用java.lang.comparable+compareTo方法中最为核心的代码,需要在业务实体类中重写compare方法,直接将该新闻类别的排序规则放入其中。

2)我们来测试一下我们的新闻排序:

package com.peng01.sort.refType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class NewsApp {
    public static void main(String[] args) {
        List<NewsItem> list = new ArrayList<NewsItem>();
        list.add(new NewsItem("中国",new Date(System.currentTimeMillis()),100));
        list.add(new NewsItem("美国",new Date(System.currentTimeMillis()-1000*60*60),50));
        list.add(new NewsItem("俄罗斯",new Date(System.currentTimeMillis()+1000*60*60),800));
        list.add(new NewsItem("日本",new Date(System.currentTimeMillis()+1000*60*60),60));
        System.out.println("排序前:"+list);
        Collections.sort(list);//直接依靠NewsItem中设定好的compareTo的规则进行比较
        System.out.println("排序后:"+list);
    }
}

在java中,时间的计算是使用毫秒来进行计算的,所以信息“中国”的时间是当前时间,“美国”是一小时之前,“俄罗斯”和“日本”的发布时间是一小时之后。相应的点击率也设置的有所不同。

3)查看一下我们的运行结果:

排序前:[标题:中国,时间:2019-01-20 09:26:42,点击率:100
, 标题:美国,时间:2019-01-20 08:26:42,点击率:50
, 标题:俄罗斯,时间:2019-01-20 10:26:42,点击率:800
, 标题:日本,时间:2019-01-20 10:26:42,点击率:60
]
排序后:[标题:俄罗斯,时间:2019-01-20 10:26:42,点击率:800
, 标题:日本,时间:2019-01-20 10:26:42,点击率:60
, 标题:中国,时间:2019-01-20 09:26:42,点击率:100
, 标题:美国,时间:2019-01-20 08:26:42,点击率:50
]

可以观察得到:首先是按照时间的最近原则进行排序,所以“俄罗斯”和“日本”的排列被放在了最前面,当时间相同时,再按照点击率进行排序,所以“俄罗斯”的点击率最高,被排列在了最前面。大家感兴趣的话可以将上面的源码直接复制,便可以运行。这个实例在排序的时候由于信息较少,还没有对标题进行排序,因为前两个时间和点击率已经完成了相应的排序规则。特此说明一下相应的标题排序,在对字符进行排序的时候,是根据计算机中的Unicode编码计算每一个字符的值,然后根据大小进行排序。

方法2、在业务类排序中使用java.util.comparactor接口,然后再自己根据需求定义相应的业务实体类compare方法。同样结合一个实例来进行讲解:

1)首先定义一个商品类:

public class Goods {
    private String name;//商品名称
    private int fav;//收藏量,喜爱
    private double price;//价格
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("商品名称:").append(this.name).append("\t");
        sb.append("收藏量:").append(this.fav).append("\t");
        sb.append("价格:").append(this.price).append("\t").append("\n");
        return sb.toString();
    }
}

由于篇幅的限制,我们就把相应的构造器还有set,get方法都省略了吧!可以看出,这个商品类是一个很纯净的简单类,里面不具有任何华丽的方法,此处重写了toString 方法是为了后续的显示好看一点,毕竟技术很重要,颜值也很重要哈!对吧!

2)然后我们再来定义两个比较器:

关于收藏量的比较器:

package com.peng01.sort.refType;
/**
 * 使用Comparator接口,设计一个专门用于价格的比较器
 * 按收藏量进行排序的业务类(升序)
 */
public class GoodsFavComp implements java.util.Comparator<Goods>{//实现comparator接口   
    //价格按照降序来进行排列
    @Override
    public int compare(Goods o1, Goods o2) {//重写compare方法
        double p1=o1.getFav();
        double p2=o2.getFav();      
        return -((p1-p2)>0?1:((p1-p2)==0?0:-1));//默认是升序,现在取-1进行降序
    }   
}

关于价格的比较器:

package com.peng01.sort.refType;
/**
 * 使用Comparator接口,设计一个专门用于价格的比较器
 */
public class GoodsPriceComp implements java.util.Comparator<Goods>{
    //价格按照降序来进行排列
    @Override
    public int compare(Goods o1, Goods o2) {
        double p1=o1.getPrice();
        double p2=o2.getPrice();        
        return -((p1-p2)>0?1:((p1-p2)==0?0:-1));//默认是升序,现在取-1进行降序
    }   
}

3)现在我们来跑一下我们的主程序:

package com.peng01.sort.refType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class GoodsApp {
    public static void main(String[] args) {
        List<Goods> goods = new ArrayList<Goods>();
        goods.add(new Goods("巧克力",3200,120));
        goods.add(new Goods("饼干",1300,10));
        goods.add(new Goods("番茄",3465,60));
        goods.add(new Goods("蔬菜",45634,54));
        goods.add(new Goods("包子",54564,14));
        System.out.println("排序前:"+"\n"+goods);
        Collections.sort(goods, new GoodsPriceComp());//依赖自己设定的GoodsPriceComp进行比较
        System.out.println("按照价格排序后:"+"\n"+goods);
        Collections.sort(goods, new GoodsFavComp());//依赖自己设定的GoodsPriceComp进行比较
        System.out.println("按照收藏排序后:"+"\n"+goods);
    }
}

现在我们来查看一下运行结果:

排序前:
[商品名称:巧克力   收藏量:3200    价格:120.0    
, 商品名称:饼干   收藏量:1300    价格:10.0
, 商品名称:番茄   收藏量:3465    价格:60.0
, 商品名称:蔬菜   收藏量:45634   价格:54.0
, 商品名称:包子   收藏量:54564   价格:14.0
]
按照价格排序后:
[商品名称:巧克力   收藏量:3200    价格:120.0    
, 商品名称:番茄   收藏量:3465    价格:60.0
, 商品名称:蔬菜   收藏量:45634   价格:54.0
, 商品名称:包子   收藏量:54564   价格:14.0
, 商品名称:饼干   收藏量:1300    价格:10.0
]
按照收藏排序后:
[商品名称:包子    收藏量:54564   价格:14.0
, 商品名称:蔬菜   收藏量:45634   价格:54.0
, 商品名称:番茄   收藏量:3465    价格:60.0
, 商品名称:巧克力  收藏量:3200    价格:120.0    
, 商品名称:饼干   收藏量:1300    价格:10.0
]

有没有感觉很爽啊!清爽得不得了!哈哈!we succeeded !

总结:

方法1:java.lang.comparable+compareTo

方法2:java.util.comparator+compare

比较:通过上面的实例可以发现,方法1,在实现的时候,需要在业务实体类中一次性将所有的排序规则实现,后期加入需要加入新的排序元素,将会改动源代码,并不具有低耦合的效果。方法2,在实现的时候,对于业务类的定义是一个很清洁简单的一个类别,在我们需要对信息进行排序的时候,我们只需要重新写一个比较器compare就可以了,然后再单独加入相应的主程序中即可,具有解耦的效果,并且更加灵活易于处理多变的排序规则。

对上述内容有疑问的同学,可以在文章下面留言啊!我们一起讨论,一起进步!

本文分享自微信公众号 - Java小白成长之路(Java_xiaobai),作者:鹏程万里

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第25次文章:行为型模式

    关注系统中对象之间的相互交互,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责,共有11种模式。

    鹏-程-万-里
  • 第23次文章:结构性模式

    前面三期我们主要介绍了4中创建型模式:单例模式、工厂模式、建造者模式、原生模式。这周我们开始进入下一大块儿的模式学习——结构性模式。

    鹏-程-万-里
  • 第8次文章:其他流

    使用方法:read(byte[] b,int off,int len) +close()

    鹏-程-万-里
  • java中的排序(自定义数据排序)--使用Collections的sort方法

        当引用类型的内置排序方式无法满足需求时可以自己实现满足既定要求的排序,有两种方式:

    wfaceboss
  • 使用两种方法让 ASP.NET Core 实现遵循 HATEOAS 结构的 RESTful API

    HATEOAS(Hypermedia as the engine of application state)是 REST 架构风格中最复杂的约束,也是构建成熟 ...

    solenovex
  • 【译】用Java创建你的第一个区块链-part2:可交易

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    凯哥Java
  • 用netty 3的channelbuffer来重写序列化类

    我们都知道用java来序列化一个对象,需要用到ObjectOutputSteam来把对象写进一个字节流ByteOutputStream,然后把字节流转成字节数组...

    算法之名
  • 设计模式之行为型模式

    将一个请求封装成一个对象 ,从而使我们可用不同请求对客户进行参数化 :对请求排队或记录请求日志 ,以及支持可撤销的操作 .也叫: 动作Action模式 ,事务t...

    时间静止不是简史
  • java基础第十五篇之IO流和递归算法

    缓冲区会在内存中创建一个8192容量的字节数组,内存的运算效率比硬盘要高的多所有只要降低到硬盘的读写次数就会提高效率.

    海仔
  • SpringBoot 中 mongo多数据源配置新姿势

    SpringBoot对常用的数据库支持外,对NoSQL 数据库也进行了封装自动化。这一篇主要讲springboot与mongo多数据源相关的配置

    码农小胖哥

扫码关注云+社区

领取腾讯云代金券