在现实生活中,我们可能会遇到需要对集合内的对象进行排序的场景,比如,有一个游戏得分排行榜,如先按照分数的高低由高到低排序,在分数相同的情况下,按照记录创建的时间由早到新的顺序排序。
在Java语言中,要实现集合内对象的排序,咱们可以采用如下两种方式来完成:
接下来,我们先使用Comparable和Comparator、结合示例来完成集合内对象排序的功能,然后,对这两种方式进行比较;最后,结合多属性排序的话,给出相对较好的实践方法。
编写游戏记录类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来实现,那么实体类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接口,然后实现Comparator的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; }
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的区别。
比如:
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
3.2 游戏记录创建时间比较器
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来做。
在上述示例中,我们采用先按照分数排序(降序),然后如果分数相等,按照记录的创建日期排序(升序)。以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来完成。
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完成多属性排序
public int compare(GameRecord r1, GameRecord r2) { return ComparisonChain.start().compare(r2.getScore(), r1.getScore()) .compare(r1.getCreateTime(), r2.getCreateTime()).result(); }
本文分享自微信公众号 - 孟君的编程札记(gh_0f0f5e0ae1de),作者:孟君
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2019-09-06
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句