1. 前言

排序算是比较高频的面试题了,节前面试了的两家公司都有问到排序问题,整理后分享给大家(文末见总结)。

通常我们想到实现排序就是 Collections 工具类的 sort() 方法,而 sort() 方法有两种:

  1. 直接调用 Collections.sort(List list) 方法进行排序(正序排序)。

  2. 调用 Collections.sort(List list,Comparator<? super T> c) ,自定义实现 Comparator 接口,重新 compareTo 方法。(相当于指定排序规则)

下面来详细说明这两个方法。

2. 什么是 Comparator ?

在看方法之前,我们先来看看这个 Comparator 到底有什么用?

Comparator ,中文意思为「比较器」,比较器的出现就是有些自定义类的 List 集合,不支持自身比较或者自身比较函数不能满足需求时,然后就可以写一个比较器来完成两个对象大小之间的比较,上边提到的方法 2 就是指定使用自定义 Comparator 来实现比较;而方法 1 是不指定 Comparator,不指定就会使用默认的排序方式(正序)。

3. List 属性的不同

List 排序一般分为「单属性排序」以及「实体属性排序」,单属性就是像是 String、Integer 等封装类(List list,List list),这些基本类型的封装类都已经实现了 Comparable 接口,并重写 compareTo() 方法,实体属性则就是我们自定义的实体类,然后想通过某些属性对象进行排序。

单属性

我们拿 Integer 类为例:

  1. List<Integer> list = new ArrayList<Integer>();

如下为 Integer 内部的实现接口截图:

所以,当我们直接调用 Collections.sort() 方法就可以实现排序效果(正序),举例说明:

这就是单属性集合的排序,直接调用 Collections.sort() 接口就能完成排序。

4. Comparable 和 Comparator 区别

然后就可能有小伙伴说,哎?你不对劲...

你上边说的是 Comparator 比较器,现在怎么 Integer 实现的是 Comparable 接口?Comparable 又是个什么鬼,这俩单词怎么长的这么像... 跟 Comparator 到底有啥区别呀?

咳咳,不要慌...

其实我们可以先看一下 Comparable 接口,该接口内部就只有一个 compareTo() 方法:

再来看看 Comparator 接口,Comparator 接口的内部方法就比较多了,当然,在这我们就关注一下 compare() 方法即可:

我们再回到 Integer 类,Integer 实现了 Comparable 接口,所以我们找一下 compareTo() 方法如下:

如上方法,compareTo() 方法内部调用了 Integer 内部的 compare() 方法,通过注释我们发现。

Integer 内部完成了数值的比较?

其实到这也有点眉目了,好多文章有这么一个说法:Comparable 属于内比较器,Comparator 属于外比较器

所谓的内比较器,我们还是以 Integer 为例,Integer 实现了 Comparable 接口,从 Integer 内部完成了数值的比较,也就是拿另外一个值跟自身比。

所谓的外比较器,就是他会拿一个单独的类来完成比较,这个时候我们就可以拿方法二来看了:

通过上面 List 的例子我们了解到了 Comparator 跟 Comparable 的使用,使用这两种方式都可以完成单属性的排序,区别就是内外部实现不同。

排序规则:

o1大于o2,返回正整数

o1等于o2,返回0

o1小于o3,返回负整数

5. 实体属性排序

因为平时工作中还是以实体属性 List 排序为主,并不会是直接 List,所以下面我们就通过一个 List 例子来分别通过 Comparator、Comparable 实现排序。

User.java

  1. public class User {
      /**用户年龄**/
        private Integer age;

        public User(Integer age){
            this.age = age;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }
5.1 Comparator 方式

代码及执行截图:

5.2 Comparable 方式

我们需要让 User.java 实体实现一个 Comparable 接口,并重写 compareTo() 方法:

  1. public class User implements Comparable<User>{

        private Integer age;

        public User(Integer age){
            this.age = age;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        @Override
        public int compareTo(User o) {
            // return this.age - o.getAge(); 正序
            return o.getAge() - this.age; // 倒序
        }
    }

然后我们测试一下:

ok,到这我们就实现了两种方式完成对实体属性对象的集合进行排序。

6. 总结

实现排序有两种方式:

  • 对象内部实现 Comparable 接口,重新 compareTo() 方法(区分正序倒序),完成内部比较,然后调用 Collections.sort() 排序。

  • 新建一个实现了 Comparator 接口的类,并重写 compare() 抽象方法

如下转自:https://my.oschina.net/sdlvzg/blog/2243766

JDK1.8之后可以很方便的对 List 进行排序,不用再写 Collections 类了。

6.1 基础类型 List 排序
  1. /* 对数字进行排序 */
    List<Integer> nums = Arrays.asList(3,1,5,2,9,8,4,10,6,7);
    nums.sort(Comparator.reverseOrder()); /* reverseOrder倒序 */
    System.err.println("倒序:"+nums);

    nums.sort(Comparator.naturalOrder()); /* naturalOrder自然排序即:正序 */
    System.err.println("正序:"+nums);

执行结果如下:

6.2 对象List单属性排序
  1. List<User> listDevs = new ArrayList<User>(){{
        add(new User(10));
        add(new User(9));
        add(new User(20));
        add(new User(4));
    }};

    System.out.println("排序前:");
    /*JAVA8的写法,循环*/
    listDevs.forEach((developer)->System.out.println(developer.getAge()));

    /*第一个写法*/
    Collections.sort(listDevs, new Comparator<User>() {
        @Override
        public int compare(User o1, User o2) {
            return o1.getAge().compareTo(o2.getAge());
        }
    });
    /*第二个写法,JAVA8的写法,List 接口支持直接使用 sort 该方法,不再需要使用 Collections.sort 了
    listDevs.sort(listDevs, new Comparator<Developer>() {
       @Override
       public int compare(Developer o1, Developer o2) {
           return o1.getAge().compareTo(o2.getAge();
       }
    });*/

    /*第三个写法,Lambda写法,JAVA8的写法
    listDevs.sort((Developer o1, Developer o2)->o1.getAge().compareTo(o2.getAge()));*/

    /*第四个写法,Lambda写法,JAVA8的写法
    listDevs.sort((o1, o2)->o1.getAge().compareTo(o2.getAge()));*/

    /*第五写法,个Lambda写法,JAVA8的写法
    listDevs.sort(Comparator.comparing(Developer::getAge));*/

    /*第六写法,个Lambda写法,JAVA8的写法*/
    Comparator<User> ageComparator = (o1, o2)->o1.getAge().compareTo(o2.getAge());
    listDevs.sort(ageComparator);       /*按上面配置的顺序取值*/
    listDevs.sort(ageComparator.reversed());    /*按上面配置的顺序反向取值*/

    System.out.println("排序后:");
    /*JAVA8的写法,循环*/
    listDevs.forEach((developer)->System.out.println(developer.getAge()));

博客园持续更新:https://www.cnblogs.com/niceyoo

执行截图:

面试官:说一下List排序方法的更多相关文章

  1. Android面试官:说说你对 Binder 驱动的了解?

    面试官提了一个问题:说说你对 binder 驱动的了解.这个问题虽有些 "面试造火箭" 的无奈,可难点就是亮点.价值所在,是筛选面试者的有效手段.如果让你回答,你能说出多少呢?我们 ...

  2. 面试官问我JVM内存结构,我真的是

    面试官:今天来聊聊JVM的内存结构吧? 候选者:嗯,好的 候选者:前几次面试的时候也提到了:class文件会被类加载器装载至JVM中,并且JVM会负责程序「运行时」的「内存管理」 候选者:而JVM的内 ...

  3. CURD系统怎么做出技术含量惊艳面试官

    在<CURD系统怎么做出技术含量--怎样引导面试>有朋友开玩笑说都用上了领域驱动了,就不叫CURD系统了吧.这里我解释一下,怕大家对DDD领域驱动设计有什么误解. DDD是为解决软件复杂性 ...

  4. 【Spring注解驱动开发】如何实现方法、构造器位置的自动装配?我这样回答让面试官很满意!

    在 冰河技术 微信公众号前面的文章中,我们介绍了如何使用注解来自动装配Spring组件.之前将的都是在来的字段上添加注解,那有没有什么方法可以实现方法.构造器位置的自动装配吗?今天我们就一起来探讨下如 ...

  5. 一些Java中不为人知的特殊方法,学完后面试官可能都没你知道的多!

    如果你用过反射并且执行过getDeclaredMethods方法的话,你可能会感到很吃惊.你会发现出现了很多源代码里没有的方法.如果你看一下这些方法的修饰符的话,可能会发现里面有些方法是volatil ...

  6. 面试官:JavaScript如何实现数组拍平(扁平化)方法?

    面试官:JavaScript如何实现数组拍平(扁平化)方法? 1 什么叫数组拍平? 概念很简单,意思是将一个"多维"数组降维,比如: // 原数组是一个"三维" ...

  7. JS十种经典排序算法,纯动画演示,学会了怼死面试官!

    十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较来决定 ...

  8. 面试官的七种武器:Java篇

    起源 自己经历过的面试也不少了,互联网的.外企的,都有.总结一下这些面试的经验,发现面试官问的问题其实不外乎几个大类,玩不出太多新鲜玩意的.细细想来,面试官拥有以下七种武器.恰似古龙先生笔下的武侠世界 ...

  9. 走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串

    原文:走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串 对大量数据进行查询时,可以应用到索引技术.索引是一种特殊类型的数据库对象,它保存着数据表中一列或者多列的排序结果,有 ...

  10. 如何写出面试官欣赏的Java单例

    单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例. 今天我们不谈单例模式的用途,只说一说如果在面试的时候面试官让你敲一段代码 ...

随机推荐

  1. [Luogu P1450] [HAOI2008]硬币购物 背包DP+容斥

    题面 传送门:https://www.luogu.org/problemnew/show/P1450 Solution 这是一道很有意思的在背包里面做容斥的题目. 首先,我们可以很轻松地想到暴力做背包 ...

  2. C# Base64解码小工具编写

    1 界面如下 2 关键代码如下 try { var callback = reqStr.Deserialize<dynamic>(); resStr = Newtonsoft.Json.J ...

  3. Spark集群和任务执行

    [前言:承接<Spark通识>篇] Spark集群组件 Spark是典型的Master/Slave架构,集群主要包括以下4个组件: Driver:Spark框架中的驱动器,运行用户编写Ap ...

  4. 左值 lvalue,右值 rvalue 和 移动语义 std::move

    参考文章: [1] 基础篇:lvalue,rvalue和move [2] 深入浅出 C++ 右值引用 [3] Modern CPP Tutorial [4] 右值引用与转移语义 刷 Leetcode ...

  5. 【老孟Flutter】6种极大提升Flutter开发效率的工具包

    老孟导读:本文介绍6种极大提升Flutter开发效率的工具包. [1] 强大的日志软件包 在开发 Flutter 的过程中打印日志是常用的调试方式之一,但 Flutter 内置的日志打印非常简单,下面 ...

  6. layui表单一

    1. ***首先明确一点 表单的以来加载模块是 form.如果不加载form模块,select.checkbox.radio等将无法显示,并且无法使用form相关功能. 我们用layui官网的样本来做 ...

  7. CSS3:overflow属性详解

    1.Overflow overflow为溢出(容器),当内容超出容器时只需添加overflow属性值为hidden, 就可以把超出容器的部分隐藏起来: 如果内容超出容器却又不想其隐藏时可以将其属性值设 ...

  8. php post请求https

    <?php $url = 'https://www.xxx.com'; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); c ...

  9. vi&vim 基本使用方法

    vi/&vim 基本使用方法 本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了!i/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅 ...

  10. Mycat分库分表(一)

    随着业务变得越来越复杂,用户越来越多,集中式的架构性能会出现巨大的问题,比如系统会越来越慢,而且时不时会宕机,所以必须要解决高性能和可用性的问题.这个时候数据库的优化就显得尤为重要,在说优化方案前,先 ...