前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Comparable和Comparator对Java集合对象进行排序

使用Comparable和Comparator对Java集合对象进行排序

作者头像
孟君
发布2019-09-09 17:36:09
5.3K0
发布2019-09-09 17:36:09
举报

在现实生活中,我们可能会遇到需要对集合内的对象进行排序的场景,比如,有一个游戏得分排行榜,如先按照分数的高低由高到低排序,在分数相同的情况下按照记录创建的时间由早到新的顺序排序

在Java语言中,要实现集合内对象的排序,咱们可以采用如下两种方式来完成:

  1. 使用Comparable来实现
  2. 使用Comparator来实现

接下来,我们先使用Comparable和Comparator、结合示例来完成集合内对象排序的功能,然后,对这两种方式进行比较;最后,结合多属性排序的话,给出相对较好的实践方法。

一、使用Comparable实现

编写游戏记录类GameRecord,然后实现Comparable接口#compareTo方法。

代码语言:javascript
复制
 public int compareTo(T o);

具体代码如下:

代码语言:javascript
复制
import java.util.Date;

import org.apache.http.client.utils.DateUtils;

/**
 * @author wangmengjun
 *
 */
public class GameRecord implements Comparable<GameRecord> {

    /**玩家名称*/
    private String name;

    /**记录创建时间*/
    private Date createTime;

    /**得分*/
    private int score;

    /**
     * @param name
     * @param createTime
     * @param score
     */
    public GameRecord(String name, Date createTime, int score) {
        this.name = name;
        this.createTime = createTime;
        this.score = score;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the createTime
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * @param createTime the createTime to set
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    /**
     * @return the score
     */
    public int getScore() {
        return score;
    }

    /**
     * @param score the score to set
     */
    public void setScore(int score) {
        this.score = score;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "GameRecord [name=" + name + ", createTime="
                + DateUtils.formatDate(createTime, "yyyy-MM-dd HH:mm:ss") + ", score=" + score
                + "]";
    }

    /* (non-Javadoc)
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    @Override
    public int compareTo(GameRecord other) {
        int scoreComapre = Integer.compare(other.getScore(), score);
        /**
         * 先按照分数从高到低排列,
         * 如果分数一样,按照记录创建的时间排序
         */
        if (scoreComapre == 0) {
            return createTime.compareTo(other.getCreateTime());
        }
        return scoreComapre;
    }

}

当GameRecord类实现Comparable接口之后,该类对象就具有比较的功能了,然后我们要做的就是对GameRecord对象的集合类进行排序即可,集合的排序可以采用java.util.Collections类的sort方法完成。

代码示例如下:

代码语言:javascript
复制
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.httpclient.util.DateParseException;
import org.apache.commons.httpclient.util.DateUtil;
import org.apache.http.client.utils.DateUtils;

/**
 * @author wangmengjun
 *
 */
public class Main {

    /**
     * 可用于转换的日期格式集合
     */
    private static final Collection<String> AVAILABLE_DATE_FORMATTERS = Arrays
            .asList("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) throws DateParseException {

        Date d1 = DateUtil.parseDate("2015-12-31 00:22:30", AVAILABLE_DATE_FORMATTERS);
        Date d2 = DateUtil.parseDate("2016-05-11 12:56:01", AVAILABLE_DATE_FORMATTERS);
        Date d3 = DateUtil.parseDate("2016-05-11 12:56:01", AVAILABLE_DATE_FORMATTERS);

        GameRecord r1 = new GameRecord("Wang", d1, 300);
        GameRecord r2 = new GameRecord("Meng", d2, 100);
        GameRecord r3 = new GameRecord("Jun", d3, 300);

        List<GameRecord> records = Arrays.asList(r3, r2, r1);
        System.out.println("排序前的结果==>");
        //System.out.println(records);
        printGameRecordInfo(records);

        Collections.sort(records);
        System.out.println("排序后的结果==>");
        printGameRecordInfo(records);

    }

    private static void printGameRecordInfo(Collection<GameRecord> records) {
        for (GameRecord record : records) {
            System.out.println(String.format("%s\t%d\t%s",
                    DateUtils.formatDate(record.getCreateTime(), "yyyy-MM-dd HH:mm:ss"),
                    record.getScore(), record.getName()));
        }
    }

}

运行结果如下:

代码语言:javascript
复制
排序前的结果==>
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng
2015-12-31 00:22:30  300  Wang
排序后的结果==>
2015-12-31 00:22:30  300  Wang
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng

二、使用Comparator来实现

如果采用Comparator来实现,那么实体类GameRecord类无需实现Comparable接口无需改变实体类的内容

我们要做的就是建立一个外部的Comparator即可。实体类GameRecord和外部Compartor代码如下:

代码语言:javascript
复制
import java.util.Date;

import org.apache.http.client.utils.DateUtils;

/**
 * @author wangmengjun
 *
 */
public class GameRecord {

    /**玩家名称*/
    private String name;

    /**记录创建时间*/
    private Date createTime;

    /**得分*/
    private int score;

    /**
     * @param name
     * @param createTime
     * @param score
     */
    public GameRecord(String name, Date createTime, int score) {
        this.name = name;
        this.createTime = createTime;
        this.score = score;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the createTime
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * @param createTime the createTime to set
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    /**
     * @return the score
     */
    public int getScore() {
        return score;
    }

    /**
     * @param score the score to set
     */
    public void setScore(int score) {
        this.score = score;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "GameRecord [name=" + name + ", createTime="
                + DateUtils.formatDate(createTime, "yyyy-MM-dd HH:mm:ss") + ", score=" + score
                + "]";
    }

}

比较器GameRecordComparator实现Comparator接口,然后实现Comparatorcompare方法:

比较分数(从高到低),如果分数一致,那么比较记录创建时间 (从早到新)

代码语言:javascript
复制
public int compare(GameRecord r1, GameRecord r2) {
        int scoreCompare =  Integer.compare(r2.getScore(), r1.getScore());
        return scoreCompare == 0 ? r1.getCreateTime().compareTo(r2.getCreateTime()) : scoreCompare;
    }

GameRecordComparator类的详细代码如下:

代码语言:javascript
复制
import java.util.Comparator;

/**
 * @author wangmengjun
 *
 */
public class GameRecordComparator implements Comparator<GameRecord> {

    /* (non-Javadoc)
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    @Override
    public int compare(GameRecord r1, GameRecord r2) {
        int scoreCompare =  Integer.compare(r2.getScore(), r1.getScore());
        return scoreCompare == 0 ? r1.getCreateTime().compareTo(r2.getCreateTime()) : scoreCompare;
    }

}

然后,我们就可以直接调用java.util.Collections类的sort方法完成排序,java.util.Collections类的sort方法源码如下:

代码语言:javascript
复制
 public static <T> void sort(List<T> list, Comparator<? super T> c) {
        Object[] a = list.toArray();
        Arrays.sort(a, (Comparator)c);
        ListIterator i = list.listIterator();
        for (int j=0; j<a.length; j++) {
            i.next();
            i.set(a[j]);
        }
    }

测试代码如下:

代码语言:javascript
复制
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.httpclient.util.DateParseException;
import org.apache.commons.httpclient.util.DateUtil;
import org.apache.http.client.utils.DateUtils;

/**
 * @author wangmengjun
 *
 */
public class Main {

    /**
     * 可用于转换的日期格式集合
     */
    private static final Collection<String> AVAILABLE_DATE_FORMATTERS = Arrays
            .asList("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) throws DateParseException {

        Date d1 = DateUtil.parseDate("2015-12-31 00:22:30", AVAILABLE_DATE_FORMATTERS);
        Date d2 = DateUtil.parseDate("2016-05-11 12:56:01", AVAILABLE_DATE_FORMATTERS);
        Date d3 = DateUtil.parseDate("2016-05-11 12:56:01", AVAILABLE_DATE_FORMATTERS);

        GameRecord r1 = new GameRecord("Wang", d1, 300);
        GameRecord r2 = new GameRecord("Meng", d2, 100);
        GameRecord r3 = new GameRecord("Jun", d3, 300);

        List<GameRecord> records = Arrays.asList(r3, r2, r1);
        System.out.println("排序前的结果==>");
        //System.out.println(records);
        printGameRecordInfo(records);

        Collections.sort(records, new GameRecordComparator());
        System.out.println("排序后的结果==>");
        printGameRecordInfo(records);

    }

    private static void printGameRecordInfo(Collection<GameRecord> records) {
        for (GameRecord record : records) {
            System.out.println(String.format("%s\t%d\t%s",
                    DateUtils.formatDate(record.getCreateTime(), "yyyy-MM-dd HH:mm:ss"),
                    record.getScore(), record.getName()));
        }
    }

}

运行结果如下:

代码语言:javascript
复制
排序前的结果==>
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng
2015-12-31 00:22:30  300  Wang
排序后的结果==>
2015-12-31 00:22:30  300  Wang
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng

通过上面的代码,我们完成了使用Comparable以及Comparator实现对象集合排序的示例,接下来,我们来简单分析一下Comparable和Comparator的区别

三、Comparable和Comparator区别

  • 采用Comparable的方法,该方法从类的内部实现对象的比较。采用Comparator的方法,是一种类外部的实现,不需要对需要排序的类(如GameRecord)进行改变,保持原有状态即可。
  • 采用Comparable的方法,因为是类内部实现的,其排序的方式只有一种方式。采用Comparator的方法,因为是外部编写比较器实现的,所以会更加灵活。我们可以编写多种比较器完成不一样的排序

比如:

3.1 游戏分数比较器

代码语言:javascript
复制
import java.util.Comparator;

/**
 * @author wangmengjun
 *
 */
public class ScoreComparator implements Comparator<GameRecord> {

    /* (non-Javadoc)
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    @Override
    public int compare(GameRecord r1, GameRecord r2) {
        return Integer.compare(r2.getScore(), r1.getScore());
    }

}
代码语言:javascript
复制
Collections.sort(records, new ScoreComparator());
代码语言:javascript
复制
排序前的结果==>
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng
2015-12-31 00:22:30  300  Wang
排序后的结果==>
2016-05-11 12:56:01  300  Jun
2015-12-31 00:22:30  300  Wang
2016-05-11 12:56:01  100  Meng

3.2 游戏记录创建时间比较器

代码语言:javascript
复制
import java.util.Comparator;

/**
 * @author wangmengjun
 *
 */
public class CreateTimeComparator implements Comparator<GameRecord> {

    /* (non-Javadoc)
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    @Override
    public int compare(GameRecord r1, GameRecord r2) {
        return r1.getCreateTime().compareTo(r2.getCreateTime());
    }

}
代码语言:javascript
复制
Collections.sort(records, new CreateTimeComparator());
代码语言:javascript
复制
排序前的结果==>
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng
2015-12-31 00:22:30  300  Wang
排序后的结果==>
2015-12-31 00:22:30  300  Wang
2016-05-11 12:56:01  300  Jun
2016-05-11 12:56:01  100  Meng

3.3 Comparator适用场景

如果不想改变原来类的状态,添加排序功能,可以使用Comparator来做。如果有多种排序策略,可以采用Comparator来做。

3.4 多属性排序方法

在上述示例中,我们采用先按照分数排序(降序),然后如果分数相等,按照记录的创建日期排序(升序)。以GameRecordComparator的compare方法为例:

代码语言:javascript
复制
    public int compare(GameRecord r1, GameRecord r2) {
        int scoreCompare =  Integer.compare(r2.getScore(), r1.getScore());
        return scoreCompare == 0 ? r1.getCreateTime().compareTo(r2.getCreateTime()) : scoreCompare;
    }

如果属性比较多,假设在分数记录创建时间之外还需要对名称等字段进行比较,那么compare方法中,我们需要一个个地对各个属性字段逐个比较,这样写的越多,我们的if语句或者三元运算符逻辑就会增多。

有没有更加简便的方式来完成多属性排序呢?答案是肯定的

下面我们就采用CompareToBuilder以及ComparisonChain来完成。

3.5 使用org.apache.commons.lang.builder.CompareToBuilder完成多属性排序

代码语言:javascript
复制
    public int compare(GameRecord r1, GameRecord r2) {

        return new CompareToBuilder().append(r2.getScore(), r1.getScore())
                .append(r1.getCreateTime(), r2.getCreateTime())
                .toComparison();

    }

3.6 使用com.google.common.collect.ComparisonChain完成多属性排序

代码语言:javascript
复制
    public int compare(GameRecord r1, GameRecord r2) {
        return ComparisonChain.start().compare(r2.getScore(), r1.getScore())
                .compare(r1.getCreateTime(), r2.getCreateTime()).result();
    }
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、使用Comparable实现
  • 二、使用Comparator来实现
  • 三、Comparable和Comparator区别
    • 3.1 游戏分数比较器
      • 3.3 Comparator适用场景
      • 3.4 多属性排序方法
        • 3.5 使用org.apache.commons.lang.builder.CompareToBuilder完成多属性排序
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档