经常在代码中用到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 空括号用于表示一组空的参数。例如 () -> 42。
5 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 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
JSONArray是java.util.ArrayList
的子类和JSONObject是java.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)