文档章节

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

王孟君
 王孟君
发布于 2016/10/13 18:35
字数 2035
阅读 1113
收藏 3

在现实生活中,我们可能会遇到需要对集合内的对象进行排序的场景,比如,

有一个游戏得分排行榜,如先按照分数的高低由高到低排序,在分数相同的情况下按照记录创建的时间由早到新的顺序排序

 

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

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

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

使用Comparable实现

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

    public int compareTo(T o);

具体代码如下:


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方法完成。

代码示例如下:


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()));
        }
    }

}

运行结果如下:

排序前的结果==>
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代码如下:


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方法:

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

    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类的详细代码如下:


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方法源码如下:

 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]);
        }
    }

测试代码如下:


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()));
        }
    }

}

运行结果如下:

排序前的结果==>
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的方法,因为是外部编写比较器实现的,所以会更加灵活。我们可以编写多种比较器完成不一样的排序

比如:

游戏分数比较器



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());
    }

}

 

Collections.sort(records, new ScoreComparator());

 

排序前的结果==>
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

游戏记录创建时间比较器


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());
    }

}
Collections.sort(records, new CreateTimeComparator());

 

排序前的结果==>
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来做。如果有多种排序策略,可以采用Comparator来做。

 

多属性排序方法

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

    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来完成。 

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

    public int compare(GameRecord r1, GameRecord r2) {

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

    }

 

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

    public int compare(GameRecord r1, GameRecord r2) {
        return ComparisonChain.start().compare(r2.getScore(), r1.getScore())
                .compare(r1.getCreateTime(), r2.getCreateTime()).result();
    }

 

© 著作权归作者所有

王孟君

王孟君

粉丝 227
博文 94
码字总数 221044
作品 0
杭州
高级程序员
私信 提问
加载中

评论(1)

剑啸月
剑啸月
萌新又来抢沙发了
Comparator与Comparable的应用

当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。 阅读过程中有任何问题,请联系egg: 邮箱:xtfggef@gmail.com 微...

mrliuze
2015/08/04
0
0
Java学习资料-Comparable和Comparator实现对象比较

当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。下面通过两个例子分别用Comparable和Comparator实现对User对象中年...

晓阳
2015/01/22
0
0
似懂非懂 Comparable和 Comparator

在java中提供了两种排序方式:Comparable和 Comparator,它们两个看起来非常的相似,在不是很了解的情况下不知道如何使用,什么情况下使用哪个进行排序,接下来就看下它们的一个区别和使用方...

tsmyk0715
2018/09/29
0
0
java底层比较器Comparable和Comparator比较对象时的使用

刚开始学习java时可能对这2个接口存在一定的疑惑,对于Comparable意思是说:可以与此对象进行比较的那些对象的类型, 此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然...

strict_nerd
2015/05/22
0
0
Comparable和Comparator的区别

相同点: Comparable和Comparator都是接口,用来做对象的比较而达到排序的目的。 不同点: Comparable需要类本身实现它,并重写compareTo方法,参数为另外一个同类的对象,排序的时候使用Col...

特拉仔
2017/10/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

本文将基于android6.0的源码,对Camera API2.0下Camera的preview的流程进行分析。在文章android6.0源码分析之Camera API2.0下的初始化流程分析中,已经对Camera2内置应用的Open即初始化流程进...

天王盖地虎626
7分钟前
0
0
java 序列化和反序列化

1. 概述 序列恢复为Java对象的过程。 对象的序列化主要有两 首先我们介绍下序列化和反序列化的概念: 序列化:把Java对象转换为字节序列的过程。 反序列化:把字节序列恢复为Java对象的过程。...

edison_kwok
18分钟前
0
0
分布式数据一致性

狼王黄师傅
59分钟前
1
0
经验

相信每位开发者在自己开发的过程中,都会反思一些问题,比如怎样提高编程能力、如何保持心态不砍产品经理、996 之后怎样恢复精力……最近开发者 Tomasz Łakomy 将他 7 年的开发生涯中学习到...

WinkJie
今天
4
0
从源码的角度来看SpringMVC

SpringMVC核心流程图 简单总结 首先请求进入DispatcherServlet 由DispatcherServlet 从HandlerMappings中提取对应的Handler 此时只是获取到了对应的Handle,然后得去寻找对应的适配器,即:H...

骚年锦时
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部