Java中的比較器(排序)


“順序“在任何一個領域里都是非常重要的一個概念,程序也不例外。不同的執行順序,能對你的執行結果產生直接影響。

既然涉及到順序,那就要求排序。所以本文討論的就是排序中使用到的比較器Comparable和Comparator。

Comparable和Comparator都是java.包下的兩個接口,從字面上看這兩個接口都是用來做比較用的,但是jdk里面不可能定義兩個功能相同的接口,所以他們肯定有不同的用處。

JDK中的Comparable和 Comparator

Comparable和Comparator接口都是為了對類進行比較,眾所周知,諸如Integer,double等基本數據類型,java可以對他們進行比較,而對於類的比較,需要人工定義比較用到的字段比較邏輯。

Comparable

Comparable可以認為是一個內比較器,實現了Comparable接口的類有一個特點,就是這些 類是可以和自己比較的。
若一個類實現了Comparable接口,就意味着該類支持排序。實現了Comparable接口的類的對象的列表或數組可以通過Collections.sort或Arrays.sort進行自動排序。
此外,**實現此接口的對象可以用作有序映射中的鍵或有序集合中的集合,無需指定比較器。**該接口定義如下

// @since 1.2 出現得還是稍微偏晚的
public interface Comparable<T> {
    // 這里入參也是T  所以是自己和自己比較的
    // 規則:this和t比較。  返回0表示兩個對象相等
    // 返回正數: this > o
    // 返回負數: this < o
    public int compareTo(T o);
}

比如如下例子:

    public static void main(String[] args) {
        Integer[] intArr = {new Integer(2), new Integer(1), new Integer(9), new Integer(5)};
        System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(intArr));  //排序前:2,1,9,5
        Arrays.sort(intArr);
        System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(intArr)); //排序后:1,2,5,9
    }

Comparator

Comparator是比較接口,我們如果需要控制某個類的次序,而該類本身不支持排序(即沒有實現Comparable接口),那么我們就可以建立一個“該類的比較器”來進行排序,這個“比較器”只需要實現Comparator接口即可。
個人認為有兩種情況可以使用實現Comparator接口的方式:

對象不支持自己和自己比較(沒有實現Comparable接口),但是又想對兩個對象進行比較(大都是這種情況)
對象實現了Comparable接口,但是開發者認為compareTo方法中的比較方式並不是自己想要的那種比較方式

// @since 1.2  JDK8該接口增加了很多默認方法,后面也會講解
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

若一個類要實現Comparator接口:它一定要實現compare(T o1, T o2) 函數,但可以不實現 equals(Object obj) 函數。
int compare(T o1, T o2) 是“比較o1和o2的大小”。返回“負數”,意味着“o1比o2小”;返回“零”,意味着“o1等於o2”;返回“正數”,意味着“o1大於o2”。
現在我們自定義一個類Person,然后給Person類自定義一個比較器。

public class Person {
    public String name;
    public Integer age;
}

// person類的Compartor比較器  泛型類型為Person
// 按照
public class PersonCompartor implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge()-o2.getAge();
    }
}

// 測試
    public static void main(String[] args) {
        Person[] people = new Person[]{new Person("fsx", 18), new Person("dy", 15)};

        System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
        Arrays.sort(people, new PersonCompartor()); // 使用自定義的比較器排序
        System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
    }

結果:
排序前:Person{name='fsx', age=18},Person{name='dy', age=15}
排序后:Person{name='dy', age=15},Person{name='fsx', age=18}

可以看到完全按照我們自己定義的比較器排序了。並且,並且,並且Person是沒有實現排序接口的哦,所以**是沒有侵入性的**

Comparable和Comparator區別比較

Comparable是排序接口,若一個類實現了Comparable接口,就意味着“該類支持排序”。而Comparator是比較器,我們若需要控制某個類的次序,可以建立一個“該類的比較器”來進行排序。解耦了~~
Comparable相當於“內部比較器”,而Comparator相當於“外部比較器”。
個性化比較:如果實現類沒有實現Comparable接口,又想對兩個類進行比較(或者實現類實現了Comparable接口,但是對compareTo方法內的比較算法不滿意),那么可以實現Comparator接口,自定義一個比較器,寫比較算法。
Comparable接口是 java.lang包下的 而 Comparator接口才是java.util包下的。(由此課件后者被歸類為一種工具)
兩種方法各有優劣, 用Comparable 簡單, 只要實現Comparable 接口的對象直接就成為一個可以比較的對象,但是需要修改源代碼。(有侵入性)
用Comparator 的好處是不需要修改源代碼, 而是另外實現一個比較器, 當某個自定義的對象需要作比較的時候,把比較器和對象一起傳遞過去就可以比大小了, 並且在Comparator里面用戶可以自己實現復雜的可以通用的邏輯,使其可以匹配一些比較簡單的對象,那樣就可以節省很多重復勞動了。

Comparator接口中的默認方法和靜態方法

JDK1.8后,在此接口中Comparator定義了好些個靜態方法和默認方法,很多時候我們能夠當作工具來使用。

default方法屬於實例的,static方法屬於類的(當然實例也可使用)

    // 逆序排序 用於集合的排序~
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

// Demo
    public static void main(String[] args) {
        Person[] people = new Person[]{new Person("fsx", 18), new Person("dy", 15)};

        System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
        Arrays.sort(people, new PersonCompartor().reversed()); // 使用自定義的比較器排序
        System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
    }

// Demo結果:這樣reversed一下 就逆序了~~~
排序前:Person{name='fsx', age=18},Person{name='dy', age=15}
排序后:Person{name='fsx', age=18},Person{name='dy', age=15}


    // 可以很方便的實現兩層排序:比如先按照年齡排序  再按照名字排序等等~~~~
    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);
        };
    }
    default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        return thenComparing(comparing(keyExtractor));
    }
    default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        return thenComparing(comparing(keyExtractor, keyComparator));
    }
    default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
        return thenComparing(comparingInt(keyExtractor));
    }
    default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
        return thenComparing(comparingLong(keyExtractor));
    }
    default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        return thenComparing(comparingDouble(keyExtractor));
    }

// Demo:
    public static void main(String[] args) {
        Person[] people = new Person[]{new Person("fsx", 18), new Person("dy", 15)};

        System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
        Arrays.sort(people, new PersonCompartor().thenComparing(Person::getName)); // 使用自定義的比較器排序
        System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
    }

下面看看靜態方法:

    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }
    // 按照自然排序的一個比較器
    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
    }
    // 它倆是對比較器進行了包裝,對null友好了
    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }
    public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(false, comparator);
    }

// Demo
    public static void main(String[] args) {
        // 放置一個null值
        Person[] people = new Person[]{new Person("fsx", 18), null, new Person("dy", 15)};

        System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
        Arrays.sort(people, Comparator.nullsFirst(new PersonCompartor())); // 使用自定義的比較器排序
        System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
    }
// 結果  把null放在了第一位
排序前:Person{name='fsx', age=18},null,Person{name='dy', age=15}
排序后:null,Person{name='dy', age=15},Person{name='fsx', age=18}

    // 它只需要一個函數,所以要求你取出來的這個字段是實現了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));
    }
    // 和comparing 方法一不同的是 該方法多了一個參數 keyComparator ,keyComparator 是創建一個自定義的比較器  注意是只比較的是key
    // 比如這樣子:Arrays.sort(people, Comparator.comparing(Person::getAge, (a1, a2) -> a2 - a1)); // 使用自定義的比較器排序
    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));
    }

    // Arrays.sort(people, Comparator.comparingInt(Person::getAge));  他們不能自定義比較器了
    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));
    }
    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));
    }
    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));
    }

Spring中的ComparableComparator和Comparators

備注此處的Comparators指的是Spring包下的:org.springframework.util.comparator.Comparators,因為JDK自帶的java.util.Comparators它是不提供給外部訪問的。

ComparableComparator

// @since 1.2.2 比較器,用於將可比較器適配比較器接口。
public class ComparableComparator<T extends Comparable<T>> implements Comparator<T> {
    // 實例 單例    這樣子的話就可以當作Comparator來使用了
    @SuppressWarnings("rawtypes")
    public static final ComparableComparator INSTANCE = new ComparableComparator();
    @Override
    public int compare(T o1, T o2) {
        return o1.compareTo(o2);
    }
}

這個適配類是Spring1.2.2就出來了,但是下面的工具:Comparators可是Spring5.0才提供

Comparators

它是Spring5.0后提供的一個工具類,里面主要是提供了一些靜態方法,來提供外部比較器。

// @since 5.0  注意使用它和直接使用Comparator的區別是,它要求你比較的對象都實現了Comparable的   否則都是不適用的
public abstract class Comparators {
    
    // 自然排序~~~~
    // Arrays.sort(people, Comparators.comparable()); 比如你這么用,是要求peple里面元素實現了Comparable接口的  否則報錯
    @SuppressWarnings("unchecked")
    public static <T> Comparator<T> comparable() {
        return ComparableComparator.INSTANCE;
    }
    // null放在最后
    @SuppressWarnings("unchecked")
    public static <T> Comparator<T> nullsLow() {
        return NullSafeComparator.NULLS_LOW;
    }
    /// null放最后  並且我們還可以提供一個自定義的比較器
    public static <T> Comparator<T> nullsLow(Comparator<T> comparator) {
        return new NullSafeComparator<>(comparator, true);
    }

    @SuppressWarnings("unchecked")
    public static <T> Comparator<T> nullsHigh() {
        return NullSafeComparator.NULLS_HIGH;
    }
    public static <T> Comparator<T> nullsHigh(Comparator<T> comparator) {
        return new NullSafeComparator<>(comparator, false);
    }
}

OrderComparator

使用OrderComparator來比較2個對象的排序順序。注意它用於Spring用來比較實現了Ordered接口的對象。
注意它@since 07.04.2003出現得非常早,所以這個類並不支持@Order注解的排序~~~

但是,PriorityOrdered接口它也是支持的,雖然它Spring2.5才出現。
另外,它是一個Comparator,所以它可以作為自定義比較器放在數組、集合里排序。形如;

public class OrderComparator implements Comparator<Object> {
    ...
    public static void sort(List<?> list) {
        if (list.size() > 1) {
            list.sort(INSTANCE);
        }
    }
    public static void sort(Object[] array) {
        if (array.length > 1) {
            Arrays.sort(array, INSTANCE);
        }
    }
    public static void sortIfNecessary(Object value) {
        if (value instanceof Object[]) {
            sort((Object[]) value);
        }
        else if (value instanceof List) {
            sort((List<?>) value);
        }
    }
}

AnnotationAwareOrderComparator

AnnotationAwareOrderComparator繼承自OrderComparator
其可以同時處理對象實現Ordered接口或@Order注解。

顯然它增強了排序能力,不僅支持Ordered接口,還支持到了@Order注解。

@Order注解@since 2.0,AnnotationAwareOrderComparator它@since 2.0.1,幾乎同時出現的
它提供了兩個靜態方法,使用非常廣泛,方便我們對數組、即可記性排序:

public class AnnotationAwareOrderComparator extends OrderComparator {

    /**
     * 用來檢查實現Ordered接口、@Order和@Priority注解
     */
    protected Integer findOrder(Object obj) {
        // 檢查常規的Ordered接口,通過子類重寫的getOrder方法返回順序值
        Integer order = super.findOrder(obj);
        if (order != null) {
            return order;
        }

        // 檢查實現@Order和@Priority注解
        if (obj instanceof Class) {
            // 在類上檢查@Order和@Priority注解,並找出順序值
            return OrderUtils.getOrder((Class<?>) obj);
        } else if (obj instanceof Method) {
            // 在方法上檢查@Order注解,並找出順序值
            Order ann = AnnotationUtils.findAnnotation((Method) obj, Order.class);
            if (ann != null) {
                return ann.value();
            }
        } else if (obj instanceof AnnotatedElement) {
            // 在注解中找@Order注解,並找出順序值
            Order ann = AnnotationUtils.getAnnotation((AnnotatedElement) obj, Order.class);
            if (ann != null) {
                return ann.value();
            }
        } else if (obj != null) {
            order = OrderUtils.getOrder(obj.getClass());
            if (order == null && obj instanceof DecoratingProxy) {
                order = OrderUtils.getOrder(((DecoratingProxy) obj).getDecoratedClass());
            }
        }

        return order;
    }

    // ==================================================================

    // 注意此處和上面的區別,它用的是自己的instance
    public static void sort(List<?> list) {
        if (list.size() > 1) {
            list.sort(INSTANCE);
        }
    }
     */
    public static void sort(Object[] array) {
        if (array.length > 1) {
            Arrays.sort(array, INSTANCE);
        }
    }
    public static void sortIfNecessary(Object value) {
        if (value instanceof Object[]) {
            sort((Object[]) value);
        }
        else if (value instanceof List) {
            sort((List<?>) value);
        }
    }
}

OrderUtils

最后介紹這個工具類,它是Spring4.1后提出的。它支持注解@Order和javax.annotation.Priority也是被支持的,因此它的getOrder()方法就是獲取order值了。

需要注意的是:先找@Order,若沒有再去找@Priority,都沒標注就返回默認值


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM