java中Comparator 和 Comparable 区别
在java中实现比较功能是很容易的。当遇到自定义类型,或比较对象不能直接进行比较,我们需要使用比较策略,通过Comparator 或 Comparable 接口即可简单实现。
示例准备
假设有一个足球队,我们想给其中运动员按照其等级进行排序,简单定义Player类:
示例代码如下:
public class Player {
private int ranking;
private String name;
private int age;
// constructor, getters, setters
@Override
public String toString() {
return this.name;
}
}
接下来,我们创建PlayerSorter类,往集合中增加一些对象,尝试使用Collections.sort方法进行排序:
@Test
public void test1() {
List<Player> footballTeam = new ArrayList<>();
Player player1 = new Player(59, "John", 20);
Player player2 = new Player(67, "Roger", 22);
Player player3 = new Player(45, "Steven", 24);
footballTeam.add(player1);
footballTeam.add(player2);
footballTeam.add(player3);
System.out.println("Before Sorting : " + footballTeam);
Collections.sort(footballTeam);
System.out.println("After Sorting : " + footballTeam);
}
上面代码运行时会报错:
The method sort(List<T>) in the type Collections
is not applicable for the arguments (ArrayList<Player>)
下面我们解释我们那里出错了。
Comparable
见名思意,Comparable接口定义了相同两个对象之间比较策略,称为类的“自然排序”。因此为了排序,我们必须让Player对象实现Comparable接口:
public class Player implements Comparable<Player> {
//...
@Override
public int compareTo(Player otherPlayer) {
return (this.getRanking() - otherPlayer.getRanking());
}
}
compareTo方法的返回值决定对象顺序。方法返回数值表明被比较对象是否小于、等于或大于传入参数对象。
运行程序,结果Player按照ranking进行排序:
Before Sorting : [John, Roger, Steven]
After Sorting : [Steven, John, Roger]
现在我们应该理解了利用Comparable接口实现对象自然排序。下面让我们看看其他类型的排序,比直接实现接口更灵活的方式。
Comparator
Comparator 接口定义了 compare(arg1, arg2) 方法,其两个参数代表比较对象,机制类似于 Comparable.compareTo() 方法.
创建比较器
为了创建Comparator(比较器),我们需要实现Comparator接口。首先,我们创建一个Comparator,使用Player的ranking属性进行排序:
public class PlayerRankingComparator implements Comparator<Player> {
@Override
public int compare(Player firstPlayer, Player secondPlayer) {
return (firstPlayer.getRanking() - secondPlayer.getRanking());
}
}
类似地也能创建Comparator,使用Player的age属性进行排序:
public class PlayerAgeComparator implements Comparator<Player> {
@Override
public int compare(Player firstPlayer, Player secondPlayer) {
return (firstPlayer.getAge() - secondPlayer.getAge());
}
}
实现比较
为了演示这个概念,我们使用Collections.sort的另一个重载方法,带有两个参数,其中第二个参数需要Comparator 实例。
使用这种方法,能够覆盖自然排序:
PlayerRankingComparator playerComparator = new PlayerRankingComparator();
Collections.sort(footballTeam, playerComparator);
运行测试结果如下:
Before Sorting : [John, Roger, Steven]
After Sorting by ranking : [Steven, John, Roger]
如果我们需要不同的排序方式,仅需要传递不同的Comparator:
PlayerAgeComparator playerComparator = new PlayerAgeComparator();
Collections.sort(footballTeam, playerComparator);
运行测试结果如下:
Before Sorting : [John, Roger, Steven]
After Sorting by age : [Roger, John, Steven]
Java 8 的比较实现
java 8提供了新的方式定义比较器,使用lambda表达式和静态工厂方法comparing()。
下面通过简单示例lambda表达式说明。
Comparator<Player> byRanking
= (Player player1, Player player2) -> player1.getRanking() - player2.getRanking();
静态工厂方法comparing()参数需要方法计算属性进行排序,返回合适的比较器示例:
Comparator<Player> byRanking = Comparator
.comparing(Player::getRanking);
Comparator<Player> byAge = Comparator
.comparing(Player::getAge);
更多java8比较实现以后单独进行解释。
Comparator Vs Comparable
Comparable 接口适合定义缺省排序方式,换句话说,是对象的主要排序方式。那么,我们已经有了Comparable ,为什么还要使用Comparator ?下面列举几个原因:
- 有时,我们不能修改类的源代码,我们想实现其对象排序,使用Comparable接口不现实。
- 使用Comparators,我们可以不修改比较类代码
- 我们可以定义多个Comparators策略,实现不同的排序方式,Comparable实现不了。
总结
本文我们学习了java的比较策略,主要介绍了Comparator 或 Comparable 接口实现方式,并对比了两者之间的差异。同时简单介绍了java8的实现方式。