java中Comparator 和 Comparable 区别

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的实现方式。

猜你喜欢

转载自blog.csdn.net/neweastsun/article/details/80371200