# Java `Comparable`和`Comparator`

原文：http://zetcode.com/java/comparablecomparator/

Java `Comparable`和`Comparator`教程展示了如何比较 Java 中具有`Comparable`和`Comparator`接口的对象。 在进行排序时，比较两个对象至关重要。

当使用自定义 Java 对象执行比较时，我们可以使用`Comparable`或`Comparator`接口。

## Java 可比对象

`Comparable`接口对实现它的每个类的对象强加了总体排序。 此排序称为类的自然排序。 该类的`compareTo()`方法必须实现以提供自然的比较。

## Java 比较器

`Comparator`接口对某些对象集合强加了整体排序。 可以将比较器传递给排序方法（例如`Collections.sort()`或`Arrays.sort()`），以实现对排序顺序的精确控制。 比较器还可以用于控制某些数据结构（例如排序集或排序映射）的顺序，或为没有自然顺序的对象集合提供排序。

## 可比对象与比较器

以下两个列表总结了两个接口之间的区别。

### Java 可比对象

*   必须定义`o1.compareTo(o2)`
*   用于实现对象的自然排序
*   我们必须修改要对其实例进行排序的类
*   在同一类
*   只有一种实现
*   在 API 中经常通过以下方式实现：字符串，包装类，日期，日历

### Java 比较器

*   必须定义`compare(o1, o2)`
*   比较类型的两个实例的多种方法-例如按年龄，姓名比较人
*   我们可以为我们无法控制的类提供比较器
*   我们可以有多个比较器的实现
*   旨在实现对第三方类实例的排序

## Java 内置比较器示例

Java 语言提供了一些内置的比较器。

`JavaBuiltInComparatorEx.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class JavaBuiltInComparatorEx {

    public static void main(String[] args) {

        List<String> words = new ArrayList<>();

        words.add("dog");
        words.add("pen");
        words.add("sky");
        words.add("rock");
        words.add("den");
        words.add("fountain");

        words.sort(Comparator.naturalOrder());
        words.forEach(System.out::println);

        words.sort(Comparator.reverseOrder());
        words.forEach(System.out::println);
    }
}

```

在示例中，我们按升序和降序对单词数组进行排序。

```java
words.sort(Comparator.naturalOrder());

```

`Comparator.naturalOrder()`返回内置的自然顺序`Comparator`。

```java
words.sort(Comparator.reverseOrder());

```

`Comparator.reverseOrder()`返回一个比较器，该比较器强加自然顺序。

## `Comparator.comparingInt`

`Comparator.comparingInt()`方法从提供的类型中提取`int`排序键，并通过该键进行比较。

`JavaBuiltInComparatorEx2.java`

```java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {

        this.age = age;
    };

    public int getAge() {

        return this.age;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }
}

public class JavaBuiltInComparatorEx2 {

    public static void main(String[] args) {

        Person p1 = new Person("Robert", 23);
        Person p2 = new Person("Monika", 18);
        Person p3 = new Person("Tom", 37);
        Person p4 = new Person("Elisabeth", 31);

        List<Person> vals = Arrays.asList( p1, p2, p3, p4 );

        vals.sort(Comparator.comparingInt(Person::getAge));
        vals.forEach(System.out::println);
    }
}

```

在示例中，我们使用`Comparator.comparingInt()`方法比较了`Person`对象的年龄。

```java
Person{name='Monika', age=18}
Person{name='Robert', age=23}
Person{name='Elisabeth', age=31}
Person{name='Tom', age=37}

```

对象按年龄排序。

## 多个比较器

通过`Comparator.thenComparing()`方法，我们可以在对对象进行排序时使用多个比较器。

`JavaMultipleComparatorsEx.java`

```java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

// Comparing list of objects by multiple object fields

class Person {

    private String name;
    private int age;
    private String city;

    public Person(String name, int age, String city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append(", city='").append(city).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

public class JavaMultipleComparatorsEx {

    public static void main(String[] args) {

        List<Person> persons = Arrays.asList(
                new Person("Peter", 23, "New York"),
                new Person("Sarah", 13, "Las Vegas"),
                new Person("Lucy", 33, "Toronto"),
                new Person("Sarah", 21, "New York"),
                new Person("Tom", 18, "Toronto"),
                new Person("Robert", 23, "San Diego"),
                new Person("Lucy", 23, "Los Angeles"),
                new Person("Sam", 36, "Dallas"),
                new Person("Elisabeth", 31, "New York"),
                new Person("Ruth", 29, "New York"),
                new Person("Sarah", 41, "New York")
        );

        persons.sort(Comparator.comparing(Person::getName)
                .thenComparing(Person::getCity)
                .thenComparing(Person::getAge));

        persons.forEach(System.out::println);
    }
}

```

我们有`Person`对象的列表。 我们先按对象名称比较对象，然后按城市比较对象，最后按年龄比较对象。

```java
persons.sort(Comparator.comparing(Person::getName)
        .thenComparing(Person::getCity)
        .thenComparing(Person::getAge));

```

`Comparator.thenComparing()`方法允许我们将乘法比较器应用于排序操作。

```java
Person{name='Elisabeth', age=31, city='New York'}
Person{name='Lucy', age=23, city='Los Angeles'}
Person{name='Lucy', age=33, city='Toronto'}
Person{name='Peter', age=23, city='New York'}
Person{name='Robert', age=23, city='San Diego'}
Person{name='Ruth', age=29, city='New York'}
Person{name='Sam', age=36, city='Dallas'}
Person{name='Sarah', age=13, city='Las Vegas'}
Person{name='Sarah', age=21, city='New York'}
Person{name='Sarah', age=41, city='New York'}
Person{name='Tom', age=18, city='Toronto'}

```

这是输出。

## Java 自定义比较器

在下一个示例中，我们创建一个自定义`Comparator`。

`JavaCustomComparator.java`

```java
package com.zetcode;

import java.util.Arrays;
import java.util.List;

public class JavaCustomComparatorEx {

    public static void main(String[] args) {

        List<String> words = Arrays.asList("pen", "blue", "atom", "to",
                "ecclesiastical", "abbey", "car", "ten", "desk", "slim",
                "journey", "forest", "landscape", "achievement", "Antarctica");

        words.sort((e1, e2) -> e1.length() - e2.length());

        words.forEach(System.out::println);

        words.sort((e1, e2) ->  e2.length() - e1.length() );

        words.forEach(System.out::println);
    }
}

```

我们有一个单词表。 这次我们根据单词的长度对其进行比较。

```java
words.sort((e1, e2) -> e1.length() - e2.length());

```

此自定义比较器用于按单词的大小按升序对单词进行排序。

```java
words.sort((e1, e2) ->  e2.length() - e1.length() );

```

在第二种情况下，单词按降序排序。

```java
to
pen
car
ten
blue
atom
desk
slim
abbey
forest
journey
landscape
Antarctica
achievement
ecclesiastical
ecclesiastical
achievement
Antarctica
landscape
journey
forest
abbey
blue
atom
desk
slim
pen
car
ten
to

```

这是输出。

## Java 自定义比较器 II

在下面的示例中，我们创建两个自定义比较器。

`.java`

```java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;

// Comparing objects with Comparator in array

class Car {

    private String name;
    private int price;

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Car{");
        sb.append("name='").append(name).append('\'');
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }
}

class CompareByPrice implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getPrice() - c2.getPrice();
    }
}

class CompareByName implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getName().compareTo(c2.getName());
    }
}

public class JavaCustomComparatorEx2 {

    public static void main(String[] args) {

        Car[] cars = {
                new Car("Volvo", 23400), new Car("Mazda", 13700),
                new Car("Porsche", 353800), new Car("Skoda", 8900),
                new Car("Volkswagen", 19900)
        };

        System.out.println("Comparison by price:");

        Arrays.sort(cars, new CompareByPrice());

        for (Car car : cars) {

            System.out.println(car);
        }

        System.out.println();

        System.out.println("Comparison by name:");

        Arrays.sort(cars, new CompareByName());

        for (Car car : cars) {

            System.out.println(car);
        }
    }
}

```

我们有一个`Car`对象数组。 我们创建两个自定义比较器，以按对象名称和价格比较对象。

```java
class CompareByPrice implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getPrice() - c2.getPrice();
    }
}
...
Arrays.sort(cars, new CompareByPrice());

```

定制的`CompareByPrice`比较器实现`Comparator`接口； 迫使我们实现`compare()`方法。 我们的实现通过价格比较汽车对象。

```java
class CompareByName implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getName().compareTo(c2.getName());
    }
}
...
Arrays.sort(cars, new CompareByName());

```

在第二种情况下，我们通过名称比较汽车对象。

```java
Comparison by price:
Car{name='Skoda', price=8900}
Car{name='Mazda', price=13700}
Car{name='Volkswagen', price=19900}
Car{name='Volvo', price=23400}
Car{name='Porsche', price=353800}

Comparison by name:
Car{name='Mazda', price=13700}
Car{name='Porsche', price=353800}
Car{name='Skoda', price=8900}
Car{name='Volkswagen', price=19900}
Car{name='Volvo', price=23400}

```

这是出乎意料的。

## Java 可比对象示例

在下面的示例中，我们将对象与`Comparable`进行比较。

`JavaComparableEx.java`

```java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;

class Card implements Comparable<Card> {

    @Override
    public int compareTo(Card o) {

        return Comparator.comparing(Card::getValue)
                .thenComparing(Card::getSuit)
                .compare(this, o);
    }

    public enum Suits {
        SPADES,
        CLUBS,
        HEARTS,
        DIAMONDS
    }

    public enum Values {
        TWO,
        THREE,
        FOUR,
        FIVE,
        SIX,
        SEVEN,
        EIGHT,
        NINE,
        TEN,
        JACK,
        QUEEN,
        KING,
        ACE,
    }

    private Suits suit;
    private Values value;

    public Card(Values value, Suits suit) {
        this.value = value;
        this.suit = suit;
    }

    public Values getValue() {
        return value;
    }

    public Suits getSuit() {
        return suit;
    }

    public void showCard() {

        value = getValue();
        suit = getSuit();

        System.out.println(value + " of " + suit);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Card{");
        sb.append("suit=").append(suit);
        sb.append(", value=").append(value);
        sb.append('}');
        return sb.toString();
    }
}

public class JavaComparableEx {

    public static void main(String[] args) {

        Card[] cards = {
                new Card(Card.Values.KING, Card.Suits.DIAMONDS),
                new Card(Card.Values.FIVE, Card.Suits.HEARTS),
                new Card(Card.Values.ACE, Card.Suits.CLUBS),
                new Card(Card.Values.NINE, Card.Suits.SPADES),
                new Card(Card.Values.JACK, Card.Suits.SPADES),
                new Card(Card.Values.JACK, Card.Suits.DIAMONDS),};

        for (Card card: cards) {

            System.out.println(card);
        }
    }
}

```

我们有`Card`对象的列表。 每张卡都有一个值，并且属于西服。 我们实现`Comparable`接口，以便为`Card`类的对象提供一些自然的排序。

```java
@Override
public int compareTo(Card o) {

    return Comparator.comparing(Card::getValue)
            .thenComparing(Card::getSuit)
            .compare(this, o);
}

```

我们实现`compareTo()`方法。 我们首先比较卡片的值，然后根据西装的值。

```java
Card{suit=HEARTS, value=FIVE}
Card{suit=SPADES, value=NINE}
Card{suit=SPADES, value=JACK}
Card{suit=DIAMONDS, value=JACK}
Card{suit=DIAMONDS, value=KING}
Card{suit=CLUBS, value=ACE}

```

这是输出。

在本教程中，我们展示了如何使用`Comparable`和`Comparator`在 Java 中比较对象。 您可能也对相关教程感兴趣： [Java 教程](/lang/java/)，[用 Java 阅读文本文件](/articles/javareadtext/)和[用 Java](/articles/javafilterlist/) 过滤列表。