Java1.8新特性之Lambda

经常在代码中用到Lambda ,结合个人经验及网络学习做一个总结性的文章,方便后续使用。

List<String> names = ...;
Collections.sort(names, (o1, o2) -> o1.compareTo(o2));
List<String> lowercaseNames = names.stream().map(name -> name.toLowerCase();).collect(Collectors.toList());

 Arrays.stream(names).forEach(System.out::println);//转成流
 Arrays.asList(names).forEach(System.out::println);//转成list

(int a, int b) -> {  return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };

Java8之Lambda相关常用方法使用

  • stream()  将集合转为流

  • collect()  将流转成集合

  • filter()      将转为流的集合过滤出满足要求的流

  • map()      将每个元素映射成新元素

  • limit()      获取n个元素

  • skip()      跳过n元素

  • skip和limit组合实现分页(对数据库的压力没有减轻,只是看着分页了)

  • distinct   去除重复元素

      //五个user对象
        User user1 = new User(1, "张三", 10);
            ......
        User user5 = new User(5, "严七", 30);
 
        //将User对象存入list
        List<User> userList = new ArrayList<>();
            ......
        userList.add(user5);
 
        //1.stram()方法:将集合装为流
        Stream<User> stream = userList.stream(); 参考文章深入理解Stream
        System.out.println(stream);//打印对象地址。java.util.stream.ReferencePipeline$Head@65f651eb
 
        //2.collect()方法:将流转为集合
        List<User> users = userList.stream().collect(Collectors.toList());
        System.out.println(users);//[User{id=1, username='张三', age=10}.... User{id=5, username='严七', age=30}]
 
        //3.filter()方法:将转为流的集合过滤出满足要求的流
        List<User> userList1 = userList.stream().//将集合转为流
                filter(user -> user.getAge() > 20).//过滤出年龄大于20的user。(类似于sql中的where user.age > 20)
                collect(Collectors.toList());//将流转回集合(便于打印观察结果)
        System.out.println(userList1);//[User{id=4, username='赵六', age=25}, User{id=5, username='严七', age=30}]
 
        //4.map()方法:将每个元素映射成新元素
        List<User> userList2 = userList.stream().filter(user -> user.getAge() > 20).//过滤出年龄大于20的user
                map(user -> {
            user.setAge(50);//执行你想要的操作,将过滤得到的user对象年龄设置为50
            return user;//每个元素都会映射产生新元素,所以map()方法要有返回值,返回新元素
        }).collect(Collectors.toList());//将流转为集合
         System.out.println(userList2);//[User{id=4, username='赵六', age=50}, User{id=5, username='严七', age=50}]
 
        //5.limit(n):获取n个元素,类似于MySQL数据库中 'limit 参数1,参数2' 关键字:参数2
        List<User> userList3 = userList.stream().limit(3).collect(Collectors.toList());//获取前三个元素。
        System.out.println(userList3);//[User{id=1, username='张三', age=10}.... User{id=3, username='王五', age=20}]
 
        //6.  skip(n):跳过n元素,类似于MySQL数据库中 'limit 参数1,参数2' 关键字:参数1
        List<User> userList4 = userList.stream().skip(2).collect(Collectors.toList());//跳过两个元素
        System.out.println(userList4);//[User{id=3, username='王五', age=20},..., User{id=5, username='严七', age=50}]
 
        //7.skip和limit组合实现分页
        List<User> userList5 = userList.stream().skip(2).limit(2).collect(Collectors.toList());//获取第二页数据(每页显示两条数据)
        System.out.println(userList5);//[User{id=3, username='王五', age=20}, User{id=4, username='赵六', age=50}]
 
        //8.  distinct:去除重复元素
        //向集合中插入一个重复元素
        userList.add(user5);
        System.out.println(userList);//..., User{id=5, username='严七', age=50}, User{id=5, username='严七', age=30}]
        List<User> userList6 = userList.stream().distinct().collect(Collectors.toList());//去重(实体类中需要实现equals()和hashCode())
        System.out.println(userList6);//[User{id=1, username='张三', age=10},..., User{id=5, username='严七', age=50}]                                    

1.lambda表达式

  维基百科对Lambda 表达式的解释是:a function (or a subroutine) defined, and possibly called, without being bound to an identifier。简单翻译过来就是,一个不需要绑定标识符(即不需要声明)且可能被调用的定义方法(子程序)。

  语法: (argument) -> (body);

      详细语法:(Type1 param1,Type2  param2, ..., paramN) -> {

             statment1;

             statment2;

            .....

             return  statmentM;

          }

  语法结构:

1 Lambda 表达式可以具有零个,一个或多个参数。
2 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如 (int a) 与刚才相同 (a)。
3 参数用小括号括起来,用逗号分隔。例如 (a, b) 或 (int a, int b) 或 (String a, int b, float c)。
4 空括号用于表示一组空的参数。例如 () -> 425 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a。
6 Lambda 表达式的正文可以包含零条,一条或多条语句。
7 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
8 如果 Lambda 表达式的正文有一条以上的语句必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。

2.为什么要用Lambda表达式

  Java 是面向对象语言,除了原始数据类型之处,Java 中的所有内容都是一个对象。而在函数式语言中,我们只需要给函数分配变量,并将这个函数作为参数传递给其它函数就可实现特定的功能。JavaScript 就是功能编程语言的典范(闭包)。

  Lambda 表达式的加入,使得 Java 拥有了函数式编程的能力。在其它语言中,Lambda 表达式的类型是一个函数;但在 Java 中,Lambda 表达式被表示为对象,因此它们必须绑定到被称为功能接口的特定对象类型。

3.Lambda的使用

  ①参数类型省略–绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类型。这样lambda表达式就变成了:

//不使用Lambda
List<String> names = ...;//这里省略list的构造
Collections.sort(names, new Comparator<String>() {
  @Override
  public int compare(String o1, String o2) {
    return o1.compareTo(o2);
  }
});

//使用Lambda
List<String> names = ...;//这里省略list的构造
Collections.sort(names, (o1, o2) -> o1.compareTo(o2));

②当lambda表达式的参数个数只有一个,可以省略小括号。lambda表达式简写为:

List<String> names = ...;//这里省略list的构造
List<String> lowercaseNames = names.stream().map((String name) -> {return name.toLowerCase();}).collect(Collectors.toList());

//简化后
List<String> lowercaseNames = names.stream().map(name -> {return name.toLowerCase();}).collect(Collectors.toList());

③当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号。lambda表达式简化为:

  param -> statement

List<String> lowercaseNames = names.stream().map(name -> name.toLowerCase()).collect(Collectors.toList());

 ④数组使用lambda

  数组不能直接在foreach中使用

 a.forEach(System.out::println);//错误
Arrays.stream(a).forEach(System.out::println);//转成流
Arrays.stream(a).boxed().collect(Collectors.toList()).forEach(System.out::println);//int [] a,转成list
Arrays.asList(a).forEach(System.out::println);//Integer [] a,转成list

⑤集合使用lambda

arrayList.forEach(System.out::println);
hashMap.forEach((k,v)->System.out.println(k+"_"+v.intValue()));

⑥JSONArray使用lambda

  例如,使用JsonArray:[{name:“John”},{name:“David”}]并返回[“John”,“David”]的列表的函数。

写了下面的代码:

import org.json.simple.JSONArray; 
import org.json.simple.JSONObject; 

public class Main { 
    public static void main(String[] args) { 
     JSONArray jsonArray = new JSONArray(); 
     jsonArray.add(new JSONObject().put("name", "John")); 
  
System.out.println(new JSONObject().put("name", "John"));//输出null
     List list = (List) jsonArray.stream().map(json -> json.toString()).collect(Collectors.toList()); 
System.out.println(list); //输出[null]
}
}
错误:
Exception in thread "main" java.lang.NullPointerException

JSONArrayjava.util.ArrayList的子类和JSONObjectjava.util.HashMap的子类。

因此,new JSONObject().put("name", "John")返回与密钥(null)关联的先前值,而不是JSONObject实例。结果,null被添加到JSONArray
解决:
JSONArray jsonArray = new JSONArray(); 
JSONObject j1 = new JSONObject(); 
j1.put ("name", "John"); 
jsonArray.add(j1); 
List list = (List) jsonArray.stream().map(json -> json.toString()).collect(Collectors.toList()); 
System.out.println(list); //结果:[{"name":"John"}]

4.方法引用

  双冒号操作符

    双冒号运算操作符是类方法的句柄,lambda 表达式的一种简写,这种简写的学名叫 eta-conversion 或者叫 η-conversion

通常的情况下:

把 x -> System.out.println(x) 简化为 System.out::println 的过程称之为 eta-conversion

把 System.out::println 转化为 x -> System.out.println(x) 的过程称之为 eta-expansion

双冒号运算就是 java 中的[方法引用]。 [方法引用]格式为:类名::方法名

注意方法名,后面没有括号“()”的。为啥不要括号,因为这样的是式子并不代表一定会调用这个方法。这种式子一般是用作Lambda表达式,Lambda有所谓懒加载嘛,不要括号就是说,看情况调用方法。

使用方法格式有三种

  • objectName::instanceMethod
  • ClassName::staticMethod
  • ClassName::instanceMethod

前两种方式类似,等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y)。

最后一种方式,等同于把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。比如String::toLowerCase等同于x->x.toLowerCase()。

表达式:    person -> person.getAge();  可以替换成    Person::getAge
表达式:    () -> new HashMap<>();  可以替换成    HashMap::new

这种 [方法引用] 或者说 [双冒号运算] 对应的参数类型是 Function<T, R> T 表示传入类型,R 表示返回类型。

比如表达式 person -> person.getAge(); 传入参数是 person,返回值是 person.getAge() ,那么方法引用Person::getAge 就对应着 Function<Person,Integer> 类型。

collected.stream().map(string -> string.toUpperCase()).collect(Collectors.toList());
可以修改为:
collected.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new));//

5.构造器引用

构造器引用语法如下:ClassName::new,把lambda表达式的参数当成ClassName构造器的参数 。

例如BigDecimal::new等同于x->new BigDecimal(x)

部分内容借鉴文章:https://segmentfault.com/a/1190000009186509

原文地址:https://www.cnblogs.com/chen2608/p/14410850.html