四时宝库

程序员的知识宝库

java8精华-函数式编程-Comparable和Comparator(九)

在本系列之前的所有文章中,没有看到的同学可以先去看看之前的文章,我们已经了解了 Java8 引入的函数式接口。

其实在 Java 8 之前就存在函数式接口。在本文中,我们将了解它们如何参与函数式编程。

最关键的两个函数接口是Comparable和Comparator。这两个接口主要用于对无序集合进行排序。这篇文章写成面试谈话。便于更好地理解。


介绍

这些是面试中会问的重要接口。事实上,到目前为止面试过很多人关于这两个接口的问题,大多数人都没有回答出来。因此,在本文中,我们将说明所有这些棘手的问题,阅读本文后,您将能够轻松地回答这些问题。

我沿着同样的思路面试很多人,以了解应聘者对这两个接口了解多少?下面指定的模拟面试将消除您对“Comparable”和“Comparator”的所有疑惑,并将帮助您回答未来面试中的任何问题。让我们从基础开始我们的面试,慢慢深入研究这两个接口。让我们开始我们的模拟面试吧。

让我们开始面试吧!

面试官:你了解Comparable和Comparator接口吗?你能说明它们的区别吗?

应聘者是的。 Comparable 接口有一个抽象方法compareTo(),它接受实现类类型的单个参数。有两种情况我们需要实现Comparable接口。

首先,在TreeMap 和 TreeSet 或任何排序的集合中会用到。因为这些集合需要一种比较对象的方法。

其次,当我们需要使用 Collections.sort() 或 Arrays.sort() 方法对对象集合进行排序时。

面试官:说的不错。但是你还没有提到 Comparator 接口?

应聘者:哦!没有。Comparator接口也有同样的用途。它还有一个抽象方法,它接受两个参数,这两个参数是我们需要比较的对象。

面试官:你能写一个Comparable接口的例子吗?

应聘者:当然可以,我将以 Student 类为例,让它实现 Comparable 接口,如下所示。

class Student implements Comparable<Student> {

    private final int id;
    private final String name;
    private final int marks;
    // Three arg Constructor
    // Getters
    ... 
    @Override
    public int compareTo(Student other) {
        if (id < other.id) {
            return -1; 
        } else if (id > other.id) {
            return 1; 
        } else {
            return 0; 
        }
    }
}

面试官:你能解释一下你写的这段代码的意思吗?

应聘者:这里我定义了一种使用学生对象的 ID 来比较学生对象的方法。无论哪个 Student 对象具有更大的 ID,都将被视为比另一个更大的 Student 对象。

面试官:但是你不觉得compareTo()方法看起来很笨拙吗?你能尝试优化它吗?

(这又是面试者经常犯的另一个错误。我想说这是因为编程能力差。我们知道返回正整数意味着第一个对象大于另一个,负数意味着小于,0意味着等于。我们需要返回的只是正数或负数或零。不需要+1或-1或0。这是面试官发现的)。

应聘者:是的。我也觉得。所有 if-else-else 块都可以用单个语句替换,如下所示。

@Override
public int compareTo(Student other) {
    return id - other.id;
}

面试官:太棒了!您能可以使用 Comparator 接口编写相同的比较代码吗?

应聘者:当然

class StudentComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.id - s2.id;
    }
}

到目前为止,面试官测试了你关于比较 Java 中对象的基本知识。面试官现在带你进入真正的游戏)。

面试官:很好,让我们继续吧。 Comparable 和 Comparator 接口具有相同的用途。但为什么我们需要两个接口来实现相同的目的呢?

(这里面试官是测试你是否知道这两个接口的区别)

应聘者:当我们需要比较我们拥有的类的对象时,使用 Comparable 接口。这意味着我们自己编写这些类。由于我们是类的所有者,因此我们可以修改这些类,以便它们可以从 Comparable 接口实现。

但也可能存在一种情况,我们需要比较我们不是其所有者的类的对象。例如,我们将来自第三方类添加到类路径中。我们无法修改这些类。在这种情况下,我们使用 Comparator 来定义比较它们的方法。

(这是一个完美且切中要害的答案。如果你说出这个答案,你的面试官会肯定会对你印象深刻。例如你如果说的是Comparable 具有带有一个参数的compareTo()方法,而 Comparator 具有带两个参数的compare()方法,你会直接被pass。 好吧,虽然应聘者给出了一个非常好的答案,但他还漏掉了面试官发现的一点。)

面试官:说的不错。这是我们在 Comparator 和 Comparable 之间进行选择时的唯一场景吗?或者您认为还有其他场景,要用到这俩个接口吗?

应聘者:思考、思考、再思考……

面试官:好的,我给你一个提示。假设我们是该类的所有者,并且Student 类实现了 Comparable 接口 。如果我们不想在compareTo()方法中指定比较方法怎么办?

应聘者:哦!我明白了。在这种情况下,我们将使用 Comparator 接口。这是第二种情况,我们需要使用 Comparator 接口。

面试官:不错你很聪明

应聘者:笑一笑哈哈哈

(由于候选人给出了答案,候选人和面试官之间建立了融洽的关系)

面试官:好的。您知道 Comparator 接口与 Lambda 有何关系吗?

应聘者:因为它有一个抽象方法,所以可以被视为一个函数式接口。我们可以为它实现 lambda 表达式。

面试官:您可以为 StudentComparator 编写两个单独的 lambda 表达式来根据 Id 和名称进行比较吗?

应聘者:如下所示

Comparator<Student> idComparator = (s1, s2) -> s1.id - s2.id;
Comparator<Student> nameComparator =
                 (s1, s2) -> s1.getName().compareTo(s2.getName();

面试官:好的。您可以使用第二个nameComparator对 Student 对象列表进行排序吗?假设您有一个 Student 对象列表,如下所示。

List<Student> students = … ;

应聘者:当然。我可以在 List 对象上使用 sort() 方法并将Comparator作为参数发送。

students.sort((s1, s2) -> s1.getName().compareTo(s2.getName()));

面试官:现在假设我有一个不同的要求?我需要对学生进行排序,首先按姓名排序,如果姓名相等,则需要按 ID 升序排序。听明白需求了吗?

应聘者:是的,

students.sort((s1, s2) -> {
    int diff = s1.getName().compareTo(s2.getName());
    if (diff == 0) {
        diff = s1.getId() - s2.getId(); 
    }
    return diff;
});

面试官:。。。如果我要求您添加另一个条件,即 ID 相等并且需要按照分数升序排序,该怎么办?

应聘者:所以我需要先按名称排序,然后按 ID 排序,最后按分数排序?我会在 if 块内添加另一个条件,如下所示。

students.sort((s1, s2) -> {
    int diff = s1.getName().compareTo(s2.getName());
    if (diff == 0) {
        diff = s1.getId() - s2.getId();
        if (diff == 0) {
            diff = s1.getMarks() - s2.getMarks();
        }
    }
    return diff;
});

(这不完全是正确的答案。如果我们再添加一个字段怎么办?面试官是对此不满意,并提出了问题,因为有更好的解决办法。)

面试官:如果我们需要再添加一个字段(比如部门编号)怎么办?假设如果分数相等,则必须按部门编号排序。还可以添加另一个条件吗?或者有什么更好的办法吗?

应聘者:思考、思考、思考……我不知道

面试官:好的。你知道Comparator接口中的的compare()和thenComparing()方法吗?

应聘者:。。。不知道

面试官:好的,面试结束后可以了解一下。

应聘者:好的

面试官:面试差不多结束了。你还有什么问题要问吗?

应聘者没有,这是一次很棒的面试,学到了很多。

面试官我会把情况反馈给HR,他们会联系你。祝你有个美好的一天。再见。

应聘者谢谢!再见。

这就是我们的面试过程。应聘者Comparator和Comparable接口有很好的理解。最后一个问题对于那些不了解comparing()thenComparing()方法的人来说有点困难。我们将在下一篇文章中介绍它们。

希望这篇文章对你有帮助!

如果喜欢这篇文章,点赞支持一下,微信搜索:京城小人物,关注我第一时间查看更多内容,最近需要面试的同学,可以点击 「链接」 获取java面试笔记!感谢支持!

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接