Zer0e's Blog

由lambda表达式引发的思考

字数统计: 1.7k阅读时长: 6 min
2020/08/29 Share

前言

lambda表达式不能说常用吧,但偶尔还是需要用到的。关于lambda表达式,是我学python的时候接触到的概念,仔细想想也有两年了。前段时间的面试有提到java中的lambda表达式,当时自己的回答还是比较笼统的,比如作为类似语法糖的存在,通常在比较器里面写,可以免写匿名类等等。我个人感觉回答的不是很好,那这篇文章就来讲讲我们一直在用的lambda表达式吧。

正文

概念

lambda表达式在很多语言中都有,那我个人比较熟悉的python与java中的lambda。那lambda表达式究竟是啥呢?根据百度百科的定义:

lambda表达式是一个匿名函数,基于数学中的λ演算得名,可以表示闭包。

这么说谁听得懂哦?没错,没有实践的概念都是天书。
那我们先来讲讲啥是匿名函数。

匿名函数

所谓匿名函数,正如字面所讲,就是没有名字的函数,正常我们在定义函数时,会定义它的名称,其他地方调用时直接用它的名称进行调用即可。
那如果我们只需要使用一次这个函数,并且不想给他取名,那这时我们就要用到匿名函数了。
所谓的lambda表达式就是匿名函数的一种表示方式。

python中的lambda表达式

正常我们在定义函数时会这么定义:

1
2
def f(x):
return x + x

以上我们定义了一个有名称的函数,而如果使用lambda表达式的话

1
g = lambda x : x + x

那这两种定义函数的方式的功能都是一样的,那通常情况下我们不会把lambda表达式赋值给任何变量,这相当于可以使用这个变量调用匿名函数,就已经不符合匿名函数的定义。
那在python中使用到lambda表达式的场景有哪些呢?

  • 最常见的当然就是一些python中的内置函数,比如作为比较器的时候,如sorted函数,sorted([1,2,3],key=lambda x : -x),又比如map函数,map(lambda x: x+1, [1,2,3])
  • 那还有就是函数的返回可以是一个匿名函数,例如return lambda x,y : x*y,这个我比较少用。
  • 还有就是可以通过lambda表达式将某个函数屏蔽,例如前端可以通过console.log = ()=>{}来屏蔽log输出,原因是把log函数给替换成了没有功能的函数,那python中也可以,比如time.sleep = lambda x : None,就可以屏蔽sleep函数。

Java中的lambda表达式

Java 8中支持了lambda表达式,那么带来的结果就是代码更加简洁。它的亮点就是可以替代匿名内部类。
java中的lambda表达式的语法如下:

1
2
3
(parameters) -> expression

(parameters) ->{ statements; }

在java 8之前假设要新建一个线程,在之前的文章我有写过,通常我们需要这么写:

1
2
3
4
5
6
7
new Thread(new Runnable() {

@Override
public void run() {
// TODO
}
}).start();

那在java 8之后我们可以通过lambda表达式省去这个匿名runnable对象。

1
2
3
4
5
new Thread(
() -> {
System.out.println("hello");
}
}).start();

当然,我们最常见的还是传入比较器的时候,例如在java 8之前,我们需要匿名内部类来重写比较器的compare方法。例如使用优先级队列需要建立堆时,

1
2
3
4
5
6
Queue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});

那这边ieda就会提示我是否转换为lambda表达式,我们转换一下

1
Queue<Integer> queue = new PriorityQueue<>((o1, o2) -> o2 - o1);

我们代码更加直观了。当然这得益于jvm自动帮助我们判断参数类型。
那为什么可以简写呢?其实我们可以直接跟进源码看看,首先是new Thread,我们可以发现Thread有个构造函数可以传入Runnable对象,而Runnable是一个接口;接着我们看PriorityQueue,也可以发现有个构造函数可以传入Comparator对象,而Comparator也是个接口。
所以不难猜测,其实能使用lambda表达式是因为有相应的构造函数与接口的存在。那书写后编译时,自动帮我们匹配进相应的构造函数,自动判断类型,等等。这就是lambda表达式所帮我们做的。

Java lambda表达式深入

那通过以上我们可以知道,lambda表达式其实作为的是一种类似语法糖的存在,可以更直观的书写代码。lambda表达式似乎与匿名内部类可以相互替换。但事实并非如此。
当我们书写一个匿名内部类时,在编译阶段我们可以发现发现多出了一个class文件,但我们使用lambda表达式后,只有一个class文件。
继续查看使用lambda表达式后编译的class文件,我们可以发现lambda表达式其实是被封装成主类的一个私有静态方法。
还有一点是this的使用,在匿名内部类中,this指向的是自己,而lambda表达式中,this指向的是外部类。

Java lambda表达式的性能问题

这个问题其实困扰了我很久,在网上找了许多文章,有说性能有损失,有说没差多少。那在我测试后发现,lambda表达式确确实实有一点性能上的损失,但是并不大。那在我做LeetCode的其中一道题目中使用到的优先级队列,那使用匿名内部类与使用lambda表达式的时间分别是40-60ms与600-700ms(大概),所以也就是说在某些用例上面,lambda确实是慢于匿名内部类的。
那关于这个问题,如果之后还有遇到会单独写写文章还有测试方法。

总结

这篇文章讲了什么是lambda表达式以及在python和java中的常用方法。那在python中,由于这门语言本身就追求简洁与方便,所以在大部分函数中传入lambda表达式本身就是十分省事的存在,并且不会把函数定义写入整个文件中,从而污染环境。
在java中,其实我个人倾向于把lambda表达式看做是一种语法糖,减少书写量,因为它能做到大部分匿名内部类的所能做的事,从代码简洁性来看肯定是更好的。
那函数式编程越加火热的今天,lambda表达式作为函数式编程的一份子,肯定是会有越来越多语言支持,甚至当成主要书写方式。

原文作者:Zer0e

原文链接:https://re0.top/2020/08/29/lambda/

发表日期:August 29th 2020, 3:00:00 pm

更新日期:August 29th 2020, 4:04:15 pm

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 概念
    2. 2.2. 匿名函数
    3. 2.3. python中的lambda表达式
    4. 2.4. Java中的lambda表达式
    5. 2.5. Java lambda表达式深入
    6. 2.6. Java lambda表达式的性能问题
  3. 3. 总结