Maison  >  Article  >  Java  >  Introduction comparative à Comparable et Comparator en Java (exemple de code)

Introduction comparative à Comparable et Comparator en Java (exemple de code)

不言
不言avant
2019-02-19 15:56:272119parcourir


Cet article vous propose une introduction comparative (exemple de code) entre Comparable et Comparator en Java. Il a une certaine valeur de référence. Les amis dans le besoin pourront s'y référer.

1. Présentation

Le tri en Java est assuré par les deux interfaces Comparable et Comparator.

Comparable signifie qu'il peut être trié, et les objets des classes qui implémentent cette interface ont automatiquement la fonction de tri.

Comparator représente un comparateur. L'objet de la classe qui implémente cette interface est un comparateur défini pour l'objet de la classe cible. Généralement, ce comparateur sera passé en paramètre.

2. Comparable

La signification chinoise de Comparable est qu'il peut être trié, ce qui signifie qu'il prend en charge la fonction de tri. Tant que notre classe implémente cette interface, les objets de cette classe auront automatiquement la possibilité d'être triés. Et cet ordre s’appelle l’ordre naturel des classes. Les listes d'objets de cette classe peuvent être triées par Collections.sort et Arrays.sort. En même temps, les instances de cette classe sont qualifiées de clés de carte triée et d'éléments d'ensemble trié.

Si a et b sont tous deux des instances de classe C qui implémentent l'interface Comparable, alors seulement lorsque le résultat de a.compareTo(b) est cohérent avec le résultat de a.equals(b), il est appelé classe C. L’ordre naturel est cohérent avec les égaux. Il est fortement recommandé de garder l'ordre naturel de la classe cohérent avec le résultat des égaux, car s'ils sont incohérents, le comportement d'une carte triée avec des objets de cette classe comme clés et d'un ensemble trié avec des objets de cette classe comme éléments sera se comporter bizarrement.

Par exemple, pour un ensemble trié qui implémente l'interface Comparable, si le résultat de a.equals(b) est faux et a.compareTo(b)==0, alors la seconde L'opération d'addition d'éléments échouera car du point de vue de l’ensemble trié, les deux sont cohérents dans le tri et cela ne sauvegarde pas les éléments en double.

En fait, l'ordre naturel des classes en Java est fondamentalement cohérent avec les égaux, à l'exception de BigDecimal, car l'ordre naturel dans BigDecimal est cohérent avec les égaux des éléments de même valeur mais de précision différente (comme 4 et 16h00) .

Analyse du code source

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

Comme vous pouvez le voir sur le code source, cette interface n'a qu'une seule méthode abstraite compareTo. Cette méthode est principalement utilisée pour définir la façon dont nos classes sont triées. La méthode compareTo est utilisée pour comparer l'élément actuel a avec l'élément spécifié b. Le résultat est une valeur int si a < entier ≪

3. Comparator

Comparator est traduit par comparateur en chinois Il peut être passé en paramètre aux méthodes Collections.sort et Arrays.sort pour spécifier un certain. objet de classe. trier par. Dans le même temps, il peut également spécifier la méthode de tri pour l'ensemble trié et la carte triée.

Semblable à Comparable, lors de la spécification d'un comparateur, il est généralement nécessaire de s'assurer que le résultat de la comparaison est cohérent avec le résultat égal. S'ils sont incohérents, le comportement de l'ensemble trié et de la carte triée correspondants deviendra également. bizarre.

Il est recommandé que la classe de comparaison implémentée implémente également l'interface java.io.Serializing pour avoir des capacités de sérialisation, car elle peut être utilisée comme méthode de tri pour les structures de données sérialisées (TreeSet, TreeMap).

Analyse du code source

@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));
    }
}

Il n'y a que deux méthodes dans l'ancienne version de Comparator, à savoir les deux premières méthodes. Toutes les méthodes par défaut suivantes sont de nouvelles méthodes ajoutées dans la version 1.8, et la 1.8 l'est. utilisé. Nouvelle fonctionnalité : Des méthodes par défaut peuvent être ajoutées à l'interface. Même avec autant de méthodes, l’interface reste une interface fonctionnelle et compare est utilisée pour définir la méthode de comparaison.

4. Comparaison entre les deux

Comparable peut être considéré comme un comparateur interne, et Comparator peut être considéré comme un comparateur externe.

Une classe peut avoir de l'ordre en implémentant l'interface Comparable, ou peut ajouter de l'ordre en spécifiant un Comparator supplémentaire.

Les fonctions des deux sont en fait les mêmes, alors ne les mélangez pas.

Regardons un exemple :

Définissez d'abord un modèle : Utilisateur

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 + "}]";
    }
}

Définissez ensuite une classe d'implémentation 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);
    }
}

Enfin Il s'agit de la classe de test : 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());
        }
    }
}

Le résultat d'exécution est :

数组排序前:[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}]

Grâce à l'exemple ci-dessus, nous avons une conclusion, c'est-à-dire qu'il existe deux façons de définir le priorité du tri, évidemment Comparateur Comparateur Le compilateur est prioritaire sur le tri interne Comparable.

5. Résumé

Comparable est triable, et les objets des classes qui implémentent cette interface ont automatiquement la fonction triable.

Comparator est un comparateur. L'implémentation de cette interface peut définir une méthode de tri pour une certaine classe.

Lorsque Comparator et Comparable existent en même temps, le premier a une priorité plus élevée.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer