java 有序集合_月薪3万以上的程序员必备技能:Comparable和Comparator(集合)

本文参考自阿里孤尽老师的《码出高效代码》一书的第六章,为了节省大家学习时间,在这里对重要知识进行了总结,如果有知识点不明白的可以关注作者,找作者要相关的具体学习资料(免费送上)。

一些大公司面试总会问一些Java集合相关的知识,如ArrayList实现原理、HashMap实现原理以及fail-fast和fail-safe机制等。所以作者准备做一个针对Java集合进行剖析的专题(后续几篇文章会全部围绕Java集合展开)。

本篇文章主要介绍:数组和集合排序用到的Comparable和Comparator两个接口sort方法中的排序算法以及JDK7中排序算法优化

Comparable和Comparator

Java中两个对象比较的方法通常用于元素排序中,常用接口为Comparable和Comparator。

  • Comparable: 自己和自己比较,属于自营性质的比较器。以-able结尾,表示自身具备某种能力,比较方法为compareTo。
  • Comparator: 第三方比较器。以-or结尾,表示自身是比较器的实践者,比较方法为compare。

常用的Integer、String等类实现了Comparable接口,如果我们需要对自定义对象按照其特性进行相关排序的话,可以实现Comparable接口自定义比较大小规则。示例代码如下:

public 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(Person o) {        //先比较年龄        if (this.age != o.age){            return this.age > o.age ? 1: -1;        }        //再比较姓名 这里使用了String实现的Comparable的compareTo方法        return this.name.compareTo(o.name);    }    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;    }}

如果这个排序规则不符合业务方需求,那么直接修改compareTo方法不是一定可行的。有以下原因:

  1. 根据开闭原则,最好不要对自己已经交付的类进行修改。
  2. 这个排序规则有可能被其他业务方在使用。
  3. 这个Person类是他人提供的类,我们没有源码。

这时候就需要用到Comparator比较器了,Comparator比较器可以让业务方根据自身需求修改排序规则。示例代码如下:

//修改为先比较姓名 再比较年龄class PersonComparator implements Comparator{    @Override    public int compare(Person o1, Person o2) {        //先比较姓名 这里使用了String实现的Comparable的compareTo方法        int result = o1.getName().compareTo(o2.getName());        if (result != 0){            return result;        }        //再比较年龄        if (o1.getAge() != o2.getAge()){            return o1.getAge() > o2.getAge() ? 1: -1;        }        return 0;    }}

Comparator具体使用:

  • Arrays.sort中作为比较器参数。
public static  void sort(T[] a, Comparator super T> c) {    if (c == null) {        sort(a);    } else {        if (LegacyMergeSort.userRequested)            legacyMergeSort(a, c);        else            TimSort.sort(a, 0, a.length, c, null, 0, 0);    }}
  • java.util.ArrayList#sort作为比较器参数。通过下面源码可以看出,这个方法最终还是调用Arrays.sort进行排序的。
 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++; }

注:约定俗成,不管是Comparatable还是Comparator,小于情况返回-1,等于情况返回0,大于情况返回1。

归并和插入排序

  • 归并排序原理:长度为1的数组是排序好的,有n个元素的集合可以看成是n个长度为1的有序子集合 ;对有序子集合进行两两归并,并保证结果子集合有序,最后得到 n/2个长度为2的有序子集合,重复上一步骤直到所有元素归并成一个长度为n的有序集合。在此排序过程中,主要工作都在归并处理中,如何使归并过程更快,或者如何减少归并次数,成为优化归并排序的重点。
  • 插入排序原理:长度为1的数组是有序的,当有了k个己排序的元素将第 k+1个元素插入己有的 个元素中合适的位置,就会得到一个长度k+1己排序的数组。假设有n个元素且已经升序排列的数组,并且在数组尾端有第n+1个元素的位置,此时如果想要添加一个新的元素并保持数组有序,根据插入排序,可以将新元素放到第n+1个位置上,然后从后向前两两比较,如果新值较小则交换位置,直到新元素到达正确的位置。

TimeSort算法

TimSort算法是归并排序(Merage Sort)与插入排序(Insertion Sort)优化后的排序算法。对于已经部分排序的数组时间复杂度最优可达O(n); 对于随机排序的数组时间复杂度为O(nlogn),平均时间复杂度为O(nlogn)。因此Java在JDK7 中使用TimSort算法取代了原来的归并排序它有两个主要优化

归并排序的分段不再从单个元素开始,而是每次先查找当前最大的排序好的数组片段run,然后对run进行扩展并利用二分排序,之后将该run其他已经排序好的run进行归并,产生排序好的大run。 引入二分排序(binarySort)。二分排序是对插入排序的优化,在插入排序中不再是从后向前逐个元素对比,而是引入了二分查找的思想,将一次查找新元素合 适位置的时间复杂度由O(n)降低到O(logn)。

最后,希望读者通过本篇文章对数组/集合排序有个新的认识,后续发布针对Java集合的使用规范,实现原理等层面进行剖析,让大家彻底搞懂Java集合!!!

END

笔者是一位热爱互联网、热爱互联网技术、热于分享的年轻人,如果您跟我一样,我愿意成为您的朋友,分享每一个有价值的知识给您。喜欢作者的同学,点赞+转发+关注哦!

点赞+转发+关注,私信作者“读书笔记”即可获得BAT大厂面试资料、高级架构师VIP视频课程等高质量技术资料。

a068af99fda1d4e1e86f57b14737bc00.png

BAT等一线互联网面试资料和VIP高级架构师视频

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值