今天看啥  ›  专栏  ›  defaultCoder

Lambda表达式

defaultCoder  · 简书  ·  · 2019-06-10 00:14

文章预览

前言

当前jdk8已经成为主流的jdk版本, 其中有一个重要的变更大家可能尚未对其进行了解, 所以今天打算跟大家讨论一下jdk8中引入的lambda表达式.

感性认识lambda表达式

或许大家或多或少听说过类似lambda表达式, 函数式编程, 但我们先不急着啃这些令人难以下咽的概念. 我们先看看使用lambda表达式会有什么样的效果.

  • 我们在没有使用lambda表达式之前, 创建一个线程, 是这样写的:
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("new Thread!");
    }
}).start();
  • 使用lambda表达式:
new Thread(
    () -> System.out.println("new Thread")
).start();

我们可以看到, 使用了lambda表达式后, 代码可以省略一些无意义代码, 我们的代码会大大简化, 当然与此同时我们的代码可读性也大大降低.
如上所示, lambda表达式的常见用法就是可以取代一些匿名内部类, 但又不仅限于此.

lambda表达式的语法
  1. 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)
  1. 如果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);
    }
}

而使用了lambdastream

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

简单解释一下上述代码:

  1. 调用List.stream()方法得到容器的Stream.
  2. 然后调用filter()方法过滤出以数字开头的字符串.
  3. 最后调用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

简单解释一下上述代码:

  1. 调用List.stream()方法得到容器的stream
  2. 然后调用filter()方法选出不以数字开头的字符串
  3. 之后调用map()方法将字符串转换成大写形式
  4. 最后调用collect()方法将结果转换成list再收集起来
  5. 使用stream循环打印一下结果

通过例子我们可以看到stream的操作是链式操作, 我们将想要的内容进行多次过滤. 如果是我们没有使用stream时, 我们需要遍历多次, 而这个链式操作看上去也像是遍历了多次才能完成这层层过滤, 而实际上: 我们不需要担心多次迭代问题, 因为stream使用的是懒运算,

stream的操作分成两类,一类是中间操作, 另一类是结束操作, 只有结束操作才会导致真正的代码执行, 中间操作只会做一些标记, 表示需要对stream进行某种操作. 这意味着可以在stream上通过关联多种操作, 但最终只需要一次迭代.

结束语

关于lambdastream今天就讨论到这里了, 如有错误请大家指正以便修改, 感谢阅读.

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览