Comparable 和 Comparator

前言

    很久沒寫博客了,今天研究了一下jdk的比較器,想着隨手寫個博客吧。java

Comparable

    首先介紹一下java.util.Comparable這個接口,該接口只有一個方法:app

/** @param   o the object to be compared.
*   @return  a negative integer, zero, or a positive integer as this object
*            is less than, equal to, or greater than the specified object.
**/

public int compareTo(T o);

再簡單的引文註釋我也會翻譯,這是原則問題。那麼這裏的意思就是,傳入一個參數,進行比較,若是調用者比該參數「大」,那麼返回正整數,反之返回負整數,相同則返回0。固然,這個」大」是由你本身去定義的。less

在jdk8的源碼中,不少類都實現了這個接口,如Date,Long,String,Integer等等。看看Date對這個接口的實現源碼吧:ide

/**
     * Compares two Dates for ordering.
     *
     * @param   anotherDate   the <code>Date</code> to be compared.
     * @return  the value <code>0</code> if the argument Date is equal to
     *          this Date; a value less than <code>0</code> if this Date
     *          is before the Date argument; and a value greater than
     *      <code>0</code> if this Date is after the Date argument.
     * @since   1.2
     * @exception NullPointerException if <code>anotherDate</code> is null.
     */
    public int compareTo(Date anotherDate) {
        long thisTime = getMillisOf(this);
        long anotherTime = getMillisOf(anotherDate);
        return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
    }

若是this的日期在傳入參數的日期以前,那麼返回負整數,反之返回正整數,相等返回0。這裏是經過比較兩個日期時間戳大小去實現的。ui

因此Comprable它就是一個很簡單的比較器規範。this

Comparator

    這個東西其實主要就是比較器的稍微高級應用。我看了源碼而且作了下翻譯,提取出了幾個關鍵的方法。spa

@FunctionalInterface
public interface Comparator<T> {

    /**
     * 比較o1 o2 若是前者較大就返回正整數,反之返回負整數,相等則返回0
     */
    int compare(T o1, T o2);

    boolean equals(Object obj);

    /**
     * 返回一個把規則逆轉的比較器   例:當前比較器指定的規則是按時間從小到大排序,那麼它會返回一個按時間從大到小的比較器
     */
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

    /**
     * 在當前比較器的基礎上拼接一個後續比較器。
     * 例如:
     * Comparator<String> cmp = Comparator.comparingInt(String::length)
     * .thenComparing(String.CASE_INSENSITIVE_ORDER);
     * 這是一個比較字符串的代碼,意思是先比較字符串的長度,若是長度相等,就用String提供的比較器去比較。
     */
    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 Comparator<T> thenComparing_back(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        Comparator<T> comparator = new Comparator<T>() {
            @Override
            public int compare(T o1, T o2) {
                int res = this.compare(o1, o2);
                if (res != 0) {
                    return res;
                }
                return other.compare(o1, o2);
            }
        };
        return comparator;
    }

    /**
     * 返回一個逆序規則
     */
    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }

    /**
     * 返回一個正常的排序規則
     */
    @SuppressWarnings("unchecked")
    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
    }

    /**
     * 返回一個容許null存在的比較器,這個比較器認爲null值比非null值小。若是兩個比較值都是null,那麼認定爲相等。
     * 若是都不是null,那麼按正常的邏輯比較。若是傳入的比較器爲null,那麼認爲全部非null值相等
     */
    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }

    /**
     * 返回一個容許null存在的比較器,這個比較器認爲null值比非null值大。若是兩個比較值都是null,那麼認定爲相等。
     * 若是都不是null,那麼按正常的邏輯比較。若是傳入的比較器爲null,那麼認爲全部非null值相等
     */
    public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(false, comparator);
    }

    /**
     * 傳入處理器Function,返回一個新的比較器。
     * 這個新的比較器先使用function處理兩個比較值key,再去比較key。
     */
    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));
    }
    
}

應用場景

  •     List的排序
List list = new ArrayList<Integer>() {{
            add(4);
            add(444);
            add(3);
            add(2);
        }};
        //按升序排列
        list.sort(null);
        //按天然排序即升序排列
        list.sort(Comparator.naturalOrder());
        //按升序的逆序即降序排列,而且容許null值
        list.sort(Comparator.nullsLast(Comparator.naturalOrder()).reversed());
        //按升序的逆序即降序排列
        Collections.sort(list,Comparator.nullsLast(Comparator.naturalOrder()).reversed());
        System.out.println(Arrays.toString(list.toArray()));
  •     comparing方法
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));
    }

    該方法是靜態方法,傳入一個function對象,規則是先用function處理兩個比較參數key,再去比較這兩個key。方法返回一個比較器,用於覆寫比較邏輯。翻譯

    comparing方法的簡單應用:code

Function<Integer, Integer> f = s -> s * s;
        Comparator<Integer> comparator = Comparator.comparing(f);
        int compare = comparator.compare(6, 7);
        System.out.println(compare);

        //比較的是6*6和7*7,輸出-1

    高級應用對象

Comparator<Integer> comparing = Comparator.comparing(Integer::intValue);
        System.out.println(comparing.compare(3,43));
        
        //比較的是3和43,輸出-1

        //上面的Integer若是換成普通對象,同樣可使用,例如對象DevelopDoc中包含getCreateTime()方法。
        List<DevelopDoc> docs=allDocs;
        Collections.sort(docs, Comparator.comparing(DevelopDoc::getCreateTime));

    這裏要提到的是「::」這個關鍵字,屬於java8的新特性,咱們能夠經過 `::` 關鍵字來訪問類的構造方法,對象方法,靜態方法。那麼如何訪問其中的方法呢?咱們先定義一個接口:

@FunctionalInterface
    static interface Te<T extends Comparable, R extends Comparable> {
        R apply(T t);
    }

    @FunctionalInterface註解的做用是,限定該接口只能有一個可實現方法,其實不加此註解也沒問題,只要不超過1個可實現方法就行,default和static方法不在範疇中。該接口定義了一個入參爲T,返回爲R的apply方法。咱們能夠這樣使用它,

Te<Integer,Integer> t = s -> s + s;
        System.out.println(t.apply(100));
       //100+100, 輸出200

也能夠這樣使用,

//intValue()是Integer類的方法
        Te<Integer, Integer> test = Integer::intValue;
        System.out.println(test.apply(100));
       //輸入100,輸出200
相關文章
相關標籤/搜索