Java 对元素进行排序 Comparator接口、Comparable接口的区别

非基本类型实现比较

Comparator 接口

表明这是一个排序器,提供了对其中的泛型元素进行排序的方法。

Comparable 接口

表明这个元素自身带有可排序的方法,这是一个可排序的元素。

定义外部比较器

实现 Comparator 接口

定义外部比较器的方式实现比较是很灵活的,我们可以对指定的元素建立不同的比较器来实现不同的比较策略而不用考虑其他的因素。比如说:分别建立按照ID 升序、降序排序的排序器,这样我们在实现排序方法的时候就不用考虑排序策略,只专注于排序算法。

主要是实现Comparator中的 int compare(T o1, T o2); 方法, 外部使用比较器进行元素比较的时候会使用这个方法来进行元素之间的比较

public class ComparableTestData implements Comparator<TestData> {

    @Override
    public int compare(TestData o1, TestData o2) {
        if (o1.getAge() > o2.getAge()) {
            return -1;
        } else if (o1.getAge() < o2.getAge()) {
            return 1;
        }
        return 0;
    }
}

code

public class DemoB {
    public static void main(String[] args) {
        ArrayList<TestData> list = new ArrayList<>();
        list.add(new TestData(1, "aa", Collections.emptyList()));
        list.add(new TestData(2, "bb", Collections.emptyList()));
        list.add(new TestData(4, "dd", Collections.emptyList()));
        list.add(new TestData(5, "ee", Collections.emptyList()));
        list.add(new TestData(3, "cc", Collections.emptyList()));

        System.out.println("---------> 排序前");
        list.forEach(System.out::println);
        // Collections.sort(list, TestData::compareTo);

        Collections.sort(list, new ComparableTestData());
        System.out.println("---------> 排序后");
        list.forEach(System.out::println);
    }
}

待比较元素指定比较方法

带比较元素实现 Comparable 接口

另外一种方法就是我们待比较元素自己指定比较方法,也就是本身就支持排序,实现方法为实现Comparable接口

public class TestData<Inner> implements Comparable<TestData> {
    private Integer age;
    private String name;
    private List<Inner> inners;

    @Override
    public int compareTo(TestData o) {
        if (o.age > this.age) {
            return 1;
        } else if (o.age.equals(this.age)) {
            return 0;
        } else {
            return -1;
        }
    }
}

code

public class DemoA {
    public static void main(String[] args) {
        ArrayList<TestData> list = new ArrayList<>();
        list.add(new TestData(1, "aa", Collections.emptyList()));
        list.add(new TestData(2, "bb", Collections.emptyList()));
        list.add(new TestData(4, "dd", Collections.emptyList()));
        list.add(new TestData(5, "ee", Collections.emptyList()));
        list.add(new TestData(3, "cc", Collections.emptyList()));


        System.out.println("---------> 排序前");
        list.forEach(System.out::println);
        Collections.sort(list);

        System.out.println("---------> 排序后");
        list.forEach(System.out::println);
    }
}

探究 Comparator Comparable 中的比较方法如何调用的

以使用 Collections.sort 方法为例进行原理探究

探究提供比较器

实现 implements Comparator

  • 需要传递 排序器
    • 排序器是 Comparator 类型
      • 并且内部泛型要为 待排序集合中的类型

此时的sost方法是 Collections 接口中的,它的具体实现由具体的集合类做出, 此时需要查看ArrayList的sort方法

    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }

ArrayList.sort

可以看到内部使用 Arrays.sort进行排序, 在并发环境下会抛出并发异常。

    @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

Arrays.sort((E[]) elementData, 0, size, c);

这里排序实现中,可以根据是否传递排序器,分别实现做出不同的排序实现。

    public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c) {
        if (c == null) {
            // 没有传递排序器
            sort(a, fromIndex, toIndex);
        } else {
            // 传递排序器, 进行范围检查
            rangeCheck(a.length, fromIndex, toIndex);
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, fromIndex, toIndex, c);
            else
                // 会直接走到这里
                TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
        }
    }

TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);

在这里排序实现中, 最后会调用 **binarySort(a, lo, hi, lo + initRunLen, c);**进行实现。

    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && 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, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
    }

binarySort

在 binarySort 方法中就可以看到调用我们传递进来的排序器进行元素之间的大小比较了。

    private static <T> void binarySort(T[] a, int lo, int hi, int start,
                                       Comparator<? super T> c) {
        assert lo <= start && start <= hi;
        if (start == lo)
            start++;
        for ( ; start < hi; start++) {
            T pivot = a[start];

            // Set left (and right) to the index where a[start] (pivot) belongs
            int left = lo;
            int right = start;
            assert left <= right;
            /*
             * Invariants:
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
            while (left < right) {
                int mid = (left + right) >>> 1;
        // 这里将会调用我们外部排序器的排序方法进行元素的排序
                if (c.compare(pivot, a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
        }

元素本身提供排序方法

元素实现 implements Comparable

此时没有传递排序器, list.sort(null); 此时是在Collection中, 联系上面的分析,我们可以直接跳到 Arrays.sort((E[]) elementData, 0, size, c); 中

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }
  • 此时是没有传递排序器, 进行 sort(a, fromIndex, toIndex);
    public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c) {
        if (c == null) {
            // 没有传递排序器
            sort(a, fromIndex, toIndex);
        } else {
            // 传递排序器, 进行范围检查
            rangeCheck(a.length, fromIndex, toIndex);
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, fromIndex, toIndex, c);
            else
                // 会直接走到这里
                TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
        }
    }

Arrays.sort

    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            // 开始调用这个方法
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }

ComparableTimeSort.sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen)

这里开始对元素进行排序了,在 binarySort 方法中可探究到最终的实现

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

ComparableTimeSort.binarySort

在binarySort 方法中我们就可以直接看到实际的操作是把 待排序元素转化为Comparable ,然后使用其排序方法进行排序

    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 
            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;
            /*
             * Invariants:
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
            while (left < right) {
                int mid = (left + right) >>> 1;
                // 开始调用 待排序对象自己实现的排序方法
                if (pivot.compareTo(a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
        }

总结

  • 实现排序的方法
    • 新建排序器
    • 待排序元素自身支持排序
  • Conllections.sort 使用的 Arrays.sort 为自己实现
    • Arrays.sort
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值