Java之Lambda表达式

write:2022-5-19

1. Lambda表达式的基本用法

eg:

import java.util.*;
public class SimpleTester {
  public static void main(String[] args) {
    String[] data = {"Tom", "Mike","Mary","Linda","Jack"};  
    List<String> names =  Arrays.asList(data);  
  
    // 方式一:传统的遍历集合的方式  
    for(String name : names) {  
      System.out.println(name);  
    }  
  
    //方式二:使用 Lambda 表达式  
    names.forEach((name) -> System.out.println(name));  
   
    //方式三:使用 Lambda 表达式 
    names.forEach(System.out::println);  
  }
}

Lambda表达式的基本语法为:
(Type1 param1, Type2 param2, …, TypeN paramN) -> {
statment1;
statment2;
//…
return statmentM;
}

从Lambda表达式的基本语法可以看出,Lambda表达式可以理解为一段带有输入参数的可执行语句块,这种语法表达方式也可称之为函数式表达。

刚才代码中的Lambda表达式:names.forEach((name) -> System.out.println(name)); 它所表达的Lambda表达式的完整语法应该是:
(String name) ->{
System.out.println(name);
return; //return语句没有返回值,因此可以省略
}
name的类型简写是因为可以从上下文推断出name的类型;

2. Lambda表达式的简化形式

2.1 参数类型可以省略

绝大多数情况下,编译器都可以从上下文环境中聪明地推断出Lambda表达式的参数类型,例如对于以上Lambda表达式,编译器能推断出name变量的类型为String,因此Lambda表达式可以简化为:
(name) -> {
System.out.println(name + “,”);
return;
}

2.2 当Lambda表达式的参数个数只有一个,可以省略小括号

以上Lambda表达式可以简化为:
name -> {
System.out.println(name + “,”);
return;
}

2.3 当Lambda表达式只包含一条语句时,可以省略大括号、语句结尾的分号

此外,当return语句没有返回值时也可以省略。以上Lambda表达式可以简化为:
name -> System.out.println(name + “,”)

2.4 Lambda表达式中符号“->”后面也可以仅包含一个普通的表达式

语法为:
(Type1 param1, Type2 param2, …, TypeN paramN) -> (expression)
例如:
(int a,int b)->(a*b+2)

从以上可以看出,Lambda表达式非常的灵活也非常的简洁;

3. Lambda表达式的用途

3.1 用Lambda表达式代替内部类

eg:

import java.util.*;
public class InnerTester {
  public static void main(String[] args) {
 
    //方式一:使用匿名内部类  
    new Thread(new Runnable() {  
      public void run() {  
         System.out.println("Hello world !");  
      }  
    }).start();  
    
    //方式二:使用Lambda表达式
    new Thread(()-> System.out.println("Hello world !")).start();  
 
    //方式三:使用Lambda表达式
    Runnable race = () -> System.out.println("Hello world !");  
    new Thread(race).start();  
  }
}
 

3.2 Lambda表达式和集合的forEach()方法

从JDK5开始,Java集合都实现了java.util.Iterable接口,它的forEach()方法能够遍历集合中的每个元素。forEach()方法的完整定义如下:default void forEach(Consumer<? super T> action)。
forEach()方法有一个Consumer 接口类型的action参数,它包含了对集合中每个元素的具体操作行为。action参数所引用的Consumer 实例必须实现Consumer接口的accept(T t)方法,在该方法中指定对参数t所执行的具体操作。

例如以下forEach()方法中的Lambda表达式相当于是Consumer类型的匿名对象,它指定对每个元素的操作为打印这个元素:
names.forEach((name) -> System.out.println(name + “,”));

Person类:

public class Person{
  private String name; //姓名
  private int age; //年龄
  
  public Person(String name,int age){
    this.name=name; 
    this.age=age;  
  }
 
  public void setName(String name){
    this.name=name;
  }
 
 public String getName(){
    return name;
  }
  ……  //setAge(int age)和getAge()方法
}

EachTester类:

import java.util.*;
public class EachTester {
  public static void main(String[] args) {
  
    List<Person> persons = new ArrayList<Person>(){  
      {  //匿名类初始化代码  
        add(new Person("Tom",21)); 
        add(new Person("Mike",32));  
        add(new Person("Linda",19));
      }  
    };

    //Lambda表达式,相当于是Consumer类型的匿名对象
    persons.forEach( (Person p) ->{//指定对每个元素的具体操作,相当于实现Consumer接口的accept(T t)方法
                  p.setAge(p.getAge()+1);
                  System.out.println(p.getName()+":"+p.getAge()); 
                }
    );        
  }
}

3.3 用Lambda表达式对集合进行排序

import java.util.*;
public class SortTester {
  public static void main(String[] args) {
    String[] data = {"Tom", "Mike","Mary","Linda","Jack"};  
    List<String> names =  Arrays.asList(data);  
    
    //方式一:通过创建匿名的Comparator实例来排序
    Comparator<String> cp=new Comparator<String>() {  //普通方法:实现Comparator接口的compare方法
      public int compare(String s1, String s2) {  
        return (s1.compareTo(s2));  
      }  
    };
    Collections.sort(names,cp);  
    //方式二:用Lambda表达式来排序
    Comparator<String> sortByName=(String s1,String s2)->(s1.compareTo(s2));  
    Collections.sort(names, sortByName);  
     //方式三:用Lambda表达式来排序
    Collections.sort(names,(String s1,String s2)->(s1.compareTo(s2)));  
     
    names.forEach(System.out::println); 	
  }
}

3.4 Lambda表达式与Stream API联合使用

Stream接口提供了一组功能强大的操纵集合的方法:
filter(Predicate<? super T> predicate):对集合中的元素进行过滤,返回包含符合条件的元素的流。
forEach(Consumer<? super T> action):遍历集合中的元素。
limit(long maxSize):返回参数maxSize所指定个数的元素。
max(Comparator<? super T> comparator):根据参数指定的比较规则,返回集合中最大的的元素。
min(Comparator<? super T> comparator):根据参数指定的比较规则,返回集合中最小的的元素。
sorted():对集合中的元素自然排序。
sorted(Comparator<? super T> comparator):根据参数指定的比较规则,对集合中的元素排序。
mapToInt(ToIntFunction<? super T> mapper):把当前的流映射为int类型的流,返回一个IntStream对象。ToIntFunction接口类型的参数指定映射方式。ToIntFunction接口有一个返回值为int类型的applyAsInt(T value)方法,该方法指定把参数value映射为int类型的方式。

怎么样得到Stream对象:
Collection接口的stream()方法返回一个Stream对象。程序可以通过这个Stream对象操纵集合中的元素。

eg:

import java.util.*;
public class ColTester {
  public static void main(String[] args) {
  
    List<Person> persons = new ArrayList<Person>(){  
      {  //匿名类初始化代码  
        add(new Person("Tom",21)); 
        add(new Person("Mike",32));  
        add(new Person("Linda",19));
        add(new Person("Mary",29));
      }  
    };

 persons.stream()    //stream()方法得到流对象
          .filter(p -> p.getAge()>20) //过滤条件为年龄大于20 
          .forEach(p -> System.out.println(p.getName()+":"+p.getAge())); 
 
    persons.stream()   
          .sorted( (p1, p2) -> (p1.getAge() - p2.getAge()) ) //按照年龄排序
          .limit(3)  //取出三个元素
          .forEach(p -> System.out.println(p.getName()+":"+p.getAge())); 
 
    int maxAge = persons.parallelStream()  //获得并行流
                        //把包含Person对象的流映射为保存其的age属性的int类型流 
                        .mapToInt(p -> p.getAge())   
                        .max()
                        .getAsInt();
    System.out.println("Max Age:"+maxAge);
  }
}
 

4. Lambda表达式中的方法引用

以下两种Lambda表达式是等价的:
names.forEach((name) -> System.out.println(name)); //有参数name的引用
或者:
names.forEach(System.out::println); //无参数的引用

在编译器能根据上下文来推断Lambda表达式的参数的场合,可以在Lambda表达式中省略参数,直接通过“::”符号来引用方法。方法引用的语法格式有以下三种:
第一种方式: objectName::instanceMethod //引用实例方法
第二种方式: ClassName::staticMethod //引用静态方法
第三种方式: ClassName::instanceMethod //引用实例方法

eg:
x->System.out.println(x)
等同于: System.out::println //引用实例方法

(x, y)->Math.max(x,y)
等同于: Math::max //引用静态方法

x->x.toLowerCase()
等同于: String::toLowerCase //引用实例方法

5. 函数式接口(FunctionalInterface)

(1)在JDK8中,定义了一个Annotation标注类型的函数式接口FunctionalInterface:
public @interface FunctionalInterface
(2)Lambda表达式只能赋值给声明为函数式接口的Java类型的变量。
(3)Consumer、Runnable、Comparator、Predicate接口都标注为函数式接口,因此可以接受Lambda表达式。
(4)以下代码把Lambda表达式赋值给一个Runnable类型的变量,这是合法的:
Runnable race = () -> System.out.println(“Hello world !”);
(5)String类没有标注为函数式接口。以下代码试图把Lambda表达式赋值给一个String类型的变量,这是非法的,会导致编译错误:
String str=()->{return “hello”.toUpperCase();};
(6)只要查阅Java API文档,就能了解一个Java类是否被标注为函数式接口。例如在Java API文档中,Runnable接口的声明如下,由此可以看出它被标注为函数式接口:@FunctionalInterface public interface Runnable

6. 课堂小结

  1. Lambda表达式本质只是一颗让编程人员更加得心应手的“语法糖”,它只存在于Java源代码中,由编译器把它转换为常规的Java类代码。
  2. Lambda表达式有点类似于方法,由参数列表和一个使用这些参数的主体(可以是一个表达式或一个代码块)组成。
  3. Lambda表达式与Stream API联合使用,可以方便地操纵集合,完成对集合中元素的排序和过滤等操作。

7. 练习题

  1. 仿照ColTester类,对persons集合过滤,过滤条件为姓名字符串的长度大于3,接着按照姓名排序,再把集合中前三个Person对象的信息打印出来。
/* 思考题1的答案 */
import java.util.*;
public class ColTester1 {
  public static void main(String[] args) {
  
    List<Person> persons = new ArrayList<Person>(){  
      {  //匿名类初始化代码  
        add(new Person("Tom",21)); 
        add(new Person("Mike",32));  
        add(new Person("Linda",19));
        add(new Person("Mary",29));
      }  
    };
 
    persons.stream()  
          .filter(p -> p.getName().length()>3) //姓名字符串的长度大于3
           .sorted( (p1, p2) -> (p1.getName().compareTo(p2.getName())) )
          .limit(3)  //取出三个元素
          .forEach(p -> System.out.println(p.getName()+":"+p.getAge())); 
  }
}
 
  1. 以下程序代码是否能编译通过?
    Integer i=()->(new Integer(var));

[答案]编译出错,提示:不兼容的类型: Integer 不是函数式接口

  1. 把以下程序代码改为使用Lambda表达式:
    new Thread(new Runnable() {
    int var=0;
    public void run() {
    for(int i=0;i<10;i++)
    System.out.println(i);
    }
    }).start();

[答案]
new Thread(()->{
for(int i=0;i<10;i++)
System.out.println(i);
}).start();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值