前言
当前jdk8
已经成为主流的jdk版本, 其中有一个重要的变更大家可能尚未对其进行了解, 所以今天打算跟大家讨论一下jdk8
中引入的lambda
表达式.
感性认识lambda表达式
或许大家或多或少听说过类似lambda
表达式, 函数式编程, 但我们先不急着啃这些令人难以下咽的概念. 我们先看看使用lambda
表达式会有什么样的效果.
- 我们在没有使用
lambda
表达式之前, 创建一个线程, 是这样写的:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("new Thread!");
}
}).start();
new Thread(
() -> System.out.println("new Thread")
).start();
我们可以看到, 使用了lambda
表达式后, 代码可以省略一些无意义代码, 我们的代码会大大简化, 当然与此同时我们的代码可读性也大大降低.
如上所示, lambda
表达式的常见用法就是可以取代一些匿名内部类, 但又不仅限于此.
lambda表达式的语法
-
lambda
中只有单个表达式的形式化表示如下:
(parameters) -> expression
若参数只有一个, 由于编译器可以判断, 可将该形参的类型定义省略
示例如下:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
- 如果Lambda表达式中要执行多个语句块,需要将多个语句块以{}进行包装,如果有返回值,需要显示指定return语句,如下所示:
(parameters) -> { statements; }
// 多个语句的lambda表达式
new Thread(() -> {
System.out.println("语句1");
System.out.println("语句2");
System.out.println("语句3");
}).start();
接下来, 再多举个常用例子来感受一下lambda表达式.
使用lambda
遍历list
:
String[] strings = { "string01", "string02", "string03", "string04"};
List<String> strList = Arrays.asList(strings);
// 以前的循环方式
for (String str : strList) {
System.out.print(str + ", ");
}
// 使用 lambda
strList.forEach((str) -> System.out.print(str + ", "));
我们可以看到其实foreach
循环已经将代码简化了, 再使用lambda
可能简化也就简化了一点点, 效果没有之前省略匿名内部类这么明显.
lambda
就这? 显然是不够看的, 所以其实lambda
还有一个重要用法是与stream
结合使用.
lambda表达式和stream
可以把Stream
看作Java Collection
的一种视图, 就像迭代器是容器的一种视图那样(但Stream
不会修改容器中的内容). 下面例子展示了Stream
的常见用法.
eg1: 假设需要从一个字符串列表中选出以数字开头的字符串并输出, 我们的通常写法需要这样写:
List<String> list = Arrays.asList("1one", "two", "three", "4four");
for(String str : list){
if(Character.isDigit(str.charAt(0))){
System.out.println(str);
}
}
而使用了lambda
和stream
List<String> list = Arrays.asList("1one", "two", "three", "4four");
list.stream()// 1.得到容器的Steam
.filter(str -> Character.isDigit(str.charAt(0)))// 2.选出以数字开头的字符串
.forEach(str -> System.out.println(str));// 3.输出字符串
// ----- console -----
// 1one
// 4four
简单解释一下上述代码:
- 调用
List.stream()
方法得到容器的Stream
.
- 然后调用
filter()
方法过滤出以数字开头的字符串.
- 最后调用
forEach()
方法输出结果.
eg2:假设需要从一个字符串列表中,选出所有不以数字开头的字符串, 将其转换成大写形式, 并把结果放到新的集合当中.
List<String> list = Arrays.asList("1one", "two", "three", "4four");
List<String> newList =
list.stream()// 1.得到容器的Stream
.filter(str -> !Character.isDigit(str.charAt(0)))// 2.选出不以数字开头的字符串
.map(String::toUpperCase)// 3.转换成大写形式
.collect(Collectors.toList());// 4.生成结果集
// 使用stream循环打印一下结果
newList.stream().forEach(str -> System.out.println(str));
// ----- console -----
// TWO
// THREE
简单解释一下上述代码:
- 调用
List.stream()
方法得到容器的stream
- 然后调用
filter()
方法选出不以数字开头的字符串
- 之后调用
map()
方法将字符串转换成大写形式
- 最后调用
collect()
方法将结果转换成list
再收集起来
- 使用
stream
循环打印一下结果
通过例子我们可以看到stream
的操作是链式操作, 我们将想要的内容进行多次过滤. 如果是我们没有使用stream
时, 我们需要遍历多次, 而这个链式操作看上去也像是遍历了多次才能完成这层层过滤, 而实际上: 我们不需要担心多次迭代问题, 因为stream
使用的是懒运算,
stream
的操作分成两类,一类是中间操作, 另一类是结束操作, 只有结束操作才会导致真正的代码执行, 中间操作只会做一些标记, 表示需要对stream
进行某种操作. 这意味着可以在stream
上通过关联多种操作, 但最终只需要一次迭代.
结束语
关于lambda
和stream
今天就讨论到这里了, 如有错误请大家指正以便修改, 感谢阅读.
………………………………