ホームページ  >  記事  >  Java  >  Java での Comparable と Comparator の比較入門 (コード例)

Java での Comparable と Comparator の比較入門 (コード例)

不言
不言転載
2019-02-19 15:56:272124ブラウズ


この記事では、Java の Comparable と Comparator についての比較紹介 (コード例) を紹介します。必要な方は参考にしてください。

1. 概要

Java での並べ替えは、Comparable と Comparator の 2 つのインターフェイスによって提供されます。

Comparable とはソートできることを意味し、このインターフェースを実装したクラスのオブジェクトは自動的にソート機能を持ちます。

Comparator はコンパレータを表します。このインターフェイスを実装するクラスのオブジェクトは、ターゲット クラスのオブジェクトに対して定義されたコンパレータです。通常、このコンパレータはパラメータとして渡されます。

2. Comparable

Comparable の中国語の意味は「並べ替えられる」、つまり並べ替え機能をサポートしていることを意味します。私たちのクラスがこのインターフェイスを実装している限り、このクラスのオブジェクトは自動的に並べ替えられるようになります。そして、この順序はクラスの自然順序と呼ばれます。このクラスのオブジェクトのリストは、Collections.sort および Arrays.sort によって並べ替えることができます。同時に、このクラスのインスタンスは、ソートされたマップのキーおよびソートされたセットの要素として修飾されます。

a と b が両方とも Comparable インターフェイスを実装するクラス C のインスタンスである場合、a.compareTo(b) の結果が a.equals(b) の結果と一致する場合にのみ呼び出されます。クラス C。自然順序は等しいと一致します。クラスの自然な順序と、equals の結果の一貫性を保つことを強くお勧めします。一貫性がない場合、このクラスのオブジェクトをキーとして持つソートされたマップと、このクラスのオブジェクトを要素として持つソートされたセットの動作が不安定になるためです。奇妙に振る舞う。

たとえば、Comparable インターフェイスを実装するソート セットの場合、a.equals(b) の結果が false で、a.compareTo(b)==0 の場合、2 番目の要素の追加操作が実行されます。ソートセットの観点からは、この 2 つはソートにおいて一貫しており、重複した要素は保存されないため、失敗します。

実際、Java のクラスの自然な順序は、BigDecimal を除いて基本的に等しいと一致します。これは、BigDecimal の自然な順序は、値は同じだが精度が異なる要素 (4 など) の等しいと一致するためです。および 4.00) 。

ソース コード分析

public interface Comparable<T> {
    public int compareTo(T o);
}

ソース コードから、インターフェイスには、compareTo という抽象メソッドが 1 つだけあることがわかります。このメソッドは、主にクラスの並べ替え方法を定義するためのものです。 CompareTo メソッドは、現在の要素 a と指定された要素 b を比較するために使用されます。 int

3. Comparator

Comparator は中国語ではコンパレータとして翻訳され、特定の内容を指定するために Collections.sort メソッドと Arrays.sort メソッドに渡すことができます。クラスオブジェクトで並べ替えます。同時に、ソートセットおよびソートマップのソート方法も指定できます。

Comparable と同様に、コンパレータを指定する場合、通常、比較結果が等しい結果と一致していることを確認する必要があります。一致しない場合、対応するソート セットおよびソート マップの動作も変わります。奇妙な。

シリアル化されたデータ構造 (TreeSet、TreeMap) の並べ替えメソッドとして使用される可能性があるため、実装されたコンパレーター クラスには、シリアル化機能を持たせるために java.io.Serializable インターフェイスも実装することをお勧めします。

ソース コード分析

@FunctionalInterface
public interface Comparator<T> {
    // 唯一的抽象方法,用于定义比较方式(即排序方式)
    // o1>o2,返回1;o1=o2,返回0;o1<o2,返回-1
    int compare(T o1, T o2);
    boolean equals(Object obj);
    // 1.8新增的默认方法:用于反序排列
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
    // 1.8新增的默认方法:用于构建一个次级比较器,当前比较器比较结果为0,则使用次级比较器比较
    default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }
    // 1.8新增默认方法:指定次级比较器的
    // keyExtractor表示键提取器,定义提取方式
    // keyComparator表示键比较器,定义比较方式
    default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        return thenComparing(comparing(keyExtractor, keyComparator));
    }
    // 1.8新增默认方法:用于执行键的比较,采用的是由键对象内置的比较方式
    default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        return thenComparing(comparing(keyExtractor));
    }
    // 1.8新增默认方法:用于比较执行int类型的键的比较
    default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
        return thenComparing(comparingInt(keyExtractor));
    }
    // 1.8新增默认方法:用于比较执行long类型的键的比较
    default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
        return thenComparing(comparingLong(keyExtractor));
    }
    // 1.8新增默认方法:用于比较执行double类型的键的比较
    default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        return thenComparing(comparingDouble(keyExtractor));
    }
    // 1.8新增静态方法:用于得到一个相反的排序的比较器,这里针对的是内置的排序方式(即继承Comparable)
    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }
    // 1.8新增静态方法:用于得到一个实现了Comparable接口的类的比较方式的比较器
    // 简言之就是将Comparable定义的比较方式使用Comparator实现
    @SuppressWarnings("unchecked")
    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
    }
    // 1.8新增静态方法:得到一个null亲和的比较器,null小于非null,两个null相等,如果全不是null,
    // 则使用指定的比较器比较,若未指定比较器,则非null全部相等返回0
    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }
    // 1.8新增静态方法:得到一个null亲和的比较器,null大于非null,两个null相等,如果全不是null,
    // 则使用指定的比较器比较,若未指定比较器,则非null全部相等返回0
    public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(false, comparator);
    }
    // 1.8新增静态方法:使用指定的键比较器用于执行键的比较
    public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        Objects.requireNonNull(keyExtractor);
        Objects.requireNonNull(keyComparator);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2));
    }
    // 1.8新增静态方法:执行键比较,采用内置比较方式,key的类必须实现Comparable
    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }
    // 1.8新增静态方法:用于int类型键的比较
    public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
    }
    // 1.8新增静态方法:用于long类型键的比较
    public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
    }
    // 1.8新增静态方法:用于double类型键的比较
    public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
    }
}

Comparator の古いバージョンには 2 つのメソッドしかなく、最初の 2 つのメソッドはすべて 1.8 で追加された新しいメソッドであり、新しいメソッドを使用します。 1.8 で追加されました。 機能: インターフェイスにデフォルトのメソッドを追加できます。メソッドが非常に多くても、インターフェイスは依然として機能インターフェイスであり、比較メソッドを定義するために Compare が使用されます。

4. 2 つの比較

Comparable は内部コンパレータ、Comparator は外部コンパレータとみなすことができます。

Comparable インターフェイスを実装することによってクラスに順序性を持たせることも、追加の Comparator を指定することによって順序性を追加することもできます。

この 2 つの機能は実際には同じなので、混同しないでください。

例を見てみましょう:

最初にモデルを定義します: User

public class User implements Serializable, Comparable<User> {
    private static final long serialVersionUID = 1L;
    private int age;
    private String name;
    public User (){}
    public User (int age, String name){
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public int compareTo(User o) {
        return this.age - o.age;
    }
    @Override
    public String toString() {
        return "[user={age=" + age + ",name=" + name + "}]";
    }
}

次に、Comparator 実装クラス MyComparator

public class MyComparator implements Comparator<User> {
    @Override
    public int compare(User o1, User o2) {
        return o1.getName().charAt(0)-o2.getName().charAt(0);
    }
}

を定義します。最後に、テスト クラス: Main

public class Main {
    public static void main(String[] args) {
        User u1 = new User(12, "xiaohua");
        User u2 = new User(10, "abc");
        User u3 = new User(15,"ccc");
        User[] users = {u1,u2,u3};
        System.out.print("数组排序前:");
        printArray(users);
        System.out.println();
        Arrays.sort(users);
        System.out.print("数组排序1后:");
        printArray(users);
        System.out.println();
        Arrays.sort(users, new MyComparator());
        System.out.print("数组排序2后:");
        printArray(users);
        System.out.println();
        Arrays.sort(users, Comparator.reverseOrder());// 针对内置的排序进行倒置
        System.out.print("数组排序3后:");
        printArray(users);
    }
    public static void printArray (User[] users) {
        for (User user:users) {
            System.out.print(user.toString());
        }
    }
}

実行結果は次のとおりです:

数组排序前:[user={age=12,name=xiaohua}][user={age=10,name=abc}][user={age=15,name=ccc}]
数组排序1后:[user={age=10,name=abc}][user={age=12,name=xiaohua}][user={age=15,name=ccc}]
数组排序2后:[user={age=10,name=abc}][user={age=15,name=ccc}][user={age=12,name=xiaohua}]
数组排序3后:[user={age=15,name=ccc}][user={age=12,name=xiaohua}][user={age=10,name=abc}]

上記の例から、ソートの優先順位を定義するには 2 つの方法があるという結論が得られます。明らかに、Comparator コンパレーターです。内部ソート Comparable よりも優先されます。

5. 概要

Comparable はソート可能であり、このインターフェイスを実装するクラスのオブジェクトは自動的にソート可能機能を持ちます。

Comparator はコンパレータであり、このインターフェイスを実装すると、特定のクラスの並べ替えメソッドを定義できます。

Comparator と Comparable が同時に存在する場合、前者が優先されます。

以上がJava での Comparable と Comparator の比較入門 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。