Comparable和Comparator的区别(Collections.sort和Arrays.sort)

Comparable

Comparable可以认为是一个  内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,
则依赖compareTo方法的实现,compareTo方法也被称为 自然比较方法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,
那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:
1、比较者大于被比较者(也就是compareTo方法参数里面的对象),那么返回正整数
2、比较者等于被比较者,那么返回0
3、比较者小于被比较者,那么返回负整数

Comparator

Comparator可以认为是是一个 外比较器 ,个人认为有两种情况可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式
Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
1、o1大于o2,返回正整数
2、o1等于o2,返回0
3、o1小于o3,返回负整数

概念看完了,直接上代码吧

public class Test {

    public static void main(String[] args) {

    /*
    为什么需要比较器?

    public interface Comparable<T>
    该接口对实现它的每个类的对象强加一个整体排序。

    @FunctionalInterface
    public interface Comparator<T>
    比较功能,对一些对象的集合施加了一个整体排序。

    jdk1.8开发文档其实已经解释过了,主要是为了 给对象排序
     */

        // 先从 String对象入手
        String str1 = "aaaaa";
        String str2 = "A";
        System.out.println(str1+"和"+str2+"的compareTo结果: "+str1.compareTo(str2));

     /*
     为什么String类 对象str1.compareTo(str2)结果返回为 32
     String 类已经实现了Comparable,重写了传入字符串比较的方法
     public int compareTo(String anotherString) {
            int len1 = value.length;                //str1的长度
            int len2 = anotherString.value.length;  //str2的长度
            int lim = Math.min(len1, len2);         //返回 len1 和 len2 中小的那个数赋值給lim
            char v1[] = value;                      // 定义暂时字符数组v1 指向 str1的value数组
            char v2[] = anotherString.value;        // 定义暂时字符数组v2 指向 str2的value数组

            int k = 0;
            while (k < lim) {                      //循环次数以 lim为准
                char c1 = v1[k];
                char c2 = v2[k];
                if (c1 != c2) {
                    return c1 - c2;                // a - A = 32
                }
                k++;
            }
            return len1 - len2;
        }
        */


        System.out.println("==================================================");
        List list = new ArrayList();
        list.add("bb");
        list.add("aaaa");
        list.add("e");
        list.add("ccc");
        list.add("fffff");
        list.add("ddd");
        System.out.println("原列表顺序:" + list);

        Collections.sort(list);
        System.out.println("默认排序: " + list);

   /*
   因为我调用的Collections.sort(list);没有传入自定义的比较器,所以会使用第一种默认的sort
   //第一种  sort(List<T> list)
   //第二种  sort(List<T> list, Comparator<? super T> c)

//debug...F7
   public static <T extends Comparable<? super T>> void sort(List<T> list) {
            list.sort(null);
   }

//debug...F7
   public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);    // 会发现c为null时Collections.sort()在默认的情况下会使用Arrays.sort()
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }


//debug...(Arrays.sort((E[]) elementData, 0, size, c))F7
    public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c) {
        if (c == null) {
            sort(a, fromIndex, toIndex); //因为c为null没有比较器,会走这个sort
        } else {
            rangeCheck(a.length, fromIndex, toIndex);
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, fromIndex, toIndex, c);
            else
                TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
        }
    }


//debug...(sort(a, fromIndex, toIndex);)F7
    public static void sort(Object[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, fromIndex, toIndex);//LegacyMergeSort(遗留下来的归并排序)
        else
            ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);//一般sort会走这个方法
    }



//debug...(ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0))F7
    static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) {
        assert a != null && lo >= 0 && lo <= hi && hi <= a.length;

        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted

        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi);
            binarySort(a, lo, hi, lo + initRunLen);
            return;
        }


        ComparableTimSort ts = new ComparableTimSort(a, work, workBase, workLen);
        int minRun = minRunLength(nRemaining);
        do {
            // Identify next run
            int runLen = countRunAndMakeAscending(a, lo, hi);

            // If run is short, extend to min(minRun, nRemaining)
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen); // 会走这个方法!!!!!!
                runLen = force;
            }

            // Push run onto pending-run stack, and maybe merge
            ts.pushRun(lo, runLen);
            ts.mergeCollapse();

            // Advance to find next run
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }



//debug...(binarySort(a, lo, lo + force, lo + runLen))F7
    private static void binarySort(Object[] a, int lo, int hi, int start) {
        assert lo <= start && start <= hi;
        if (start == lo)
            start++;
        for ( ; start < hi; start++) {
            Comparable pivot = (Comparable) a[start];
            // Set left (and right) to the index where a[start] (pivot) belongs
            int left = lo;
            int right = start;
            assert left <= right;

        while (left < right) {
            int mid = (left + right) >>> 1;
            if (pivot.compareTo(a[mid]) < 0)    //会走String的compareTo()方法
                right = mid;
            else
                left = mid + 1;
        }
        assert left == right;

        int n = start - left;  // The number of elements to move

        switch (n) {
            case 2:  a[left + 2] = a[left + 1];
            case 1:  a[left + 1] = a[left];
                break;
            default: System.arraycopy(a, left, a, left + 1, n);
        }
        a[left] = pivot;
      }
    }

    */


        Collections.sort(list, new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).length() - ((String) o2).length();//根据字符串长度排序
            }
        });
        System.out.println("根据字符串长度排序: " + list);


        System.out.println("==================================================");

        Person person1 = new Person("A", 12);
        Person person2 = new Person("BBB", 8);
        Person person3 = new Person("CC", 20);
        Person person4 = new Person("EEEE", 1);


        List plist = new ArrayList();
        plist.add(person1);
        plist.add(person2);
        plist.add(person3);
        plist.add(person4);
        System.out.println("原(Person)列表顺序:" + plist);

        Collections.sort(plist);
        System.out.println("原(Person)列表根据年龄大小排序: " + plist);

        Collections.sort(plist,new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getName().length() - o2.getName().length();//根据字符串长度排序
            }
        });
        System.out.println("原(Person)列表根据名字长度排序: " + plist);
//根据以下结果可以看出,如果我们对内比较器Comparable算法不满意除了重写内比较器算法,还可以通过外比较器Comparator重新定义一个外比较器算法,覆盖内比较器



    }


}

class Person implements Comparable {

    private String name;
    private int age;

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

    @Override
    public int compareTo(Object o) {
        return this.age - ((Person) o).age;//根据年纪排序
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

运行结果如下:

aaaaa和A的compareTo结果: 32
==================================================
原列表顺序:[bb, aaaa, e, ccc, fffff, ddd]
默认排序: [aaaa, bb, ccc, ddd, e, fffff]
根据字符串长度排序: [e, bb, ccc, ddd, aaaa, fffff]
==================================================
原(Person)列表顺序:[Person{name='A', age=12}, Person{name='BBB', age=8}, Person{name='CC', age=20}, Person{name='EEEE', age=1}]
原(Person)列表根据年龄大小排序: [Person{name='EEEE', age=1}, Person{name='BBB', age=8}, Person{name='A', age=12}, Person{name='CC', age=20}]
原(Person)列表根据名字长度排序: [Person{name='A', age=12}, Person{name='CC', age=20}, Person{name='BBB', age=8}, Person{name='EEEE', age=1}]

总结

总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:

1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,重写比较算法

2、实现Comparable接口的方式比实现Comparator接口的耦合性 要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。

所以开发者还是需要根据具体场景选择最合适的那种比较器。