一、Lambda表达式简介
个人理解,lambda表达式就是一种新的语法,没有什么新奇的,简化了开发者的编码,其实底层还是一些常规的代码。Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
- Lambda表达式本质只是一颗让编程人员更加得心应手的“语法糖”,它只存在于Java源代码中,由编译器把它转换为常规的Java类代码。
- Lambda表达式有点类似于方法,由参数列表和一个使用这些参数的主体(可以是一个表达式或一个代码块)组成
- Lambda表达式与 Stream API 联合使用,可以方便的操纵集合,完成对集合中元素的排序和过滤等操作
Lambda表达式的语法
Lambda 表达式的基础语法:Java8中引入了一个新的操作符 "- >" 该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分:
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
new Thread( () -> System.out.println("thread"));
针对这种实行,我们怎么理解呢?其实很简单,上看一下上述lambda表达式的语法:() -> {}:
括号就是接口方法的括号,接口方法如果有参数,也需要写参数。只有一个参数时,括号可以省略。-> : 分割左右部分的,没啥好讲的。{} : 要实现的方法体。只有一行代码时,可以不加括号,可以不写return。
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
案例
@Test
public void test1(){
String[] data = {"marvin","jerry","sally","max","jason"};
List<String> names = Arrays.asList(data);
System.out.println("方式一:传统的遍历集合方式");
for(String name : names){
System.out.println(name);
}
System.out.println("方式二:使用Lambda表达式");
names.forEach((name) -> System.out.println(name));
System.out.println("方式三:使用Lambda表达式");
names.forEach(System.out::println);
}
解读:forEach 方法的参数是 Consumer 接口
public void forEach(Consumer<? super E> action)
Consumer 接口中只有一个实现方法 void accept(T t);
而 names.forEach((name) -> System.out.println(name));相当于实现了accept方法
左边
name:是入参
右边
System.out.println(name) :是accept方法的实现
二、Lambda表达式中的方法引用
在编译器能根据上下文来推断Lambda表达式的参数的场合时,可以在Lambda表达式中省略参数,直接通过 “::” 符号来引用方法
方法引用的语法格式有以下三种:
- 第一种:objectName :: instanceMethod //对象引用实例方法
- 第二种:ClassName :: staticMethod //类引用静态方法
- 第三种:ClassName :: InstanceMethod //类引用实例方法
以下两种Lambda表达式是等价的
names.forEach((name)->System.out.println(name));
等同于:names.forEach(System.out::println);
x->System.out.println(x) 等同于:System.out :: println //引用实例方法 |
(x,y)->Math.max(x,y) 等同于:Math :: max //引用静态方法 |
x->x.toLowerCase() 等同于:String :: toLowerCase //引用实例方法 |
三、函数式接口(FunctionalInterface)
在JDK8中,定义了一个 @FunctionalInterface 注解
Lambda表达式 只能赋值给 声明为 函数式接口 的Java类型的 变量
如:Consumer、Runnable、Comparator、Predicate接口都标注为函数式接口,因此可以接收Lambda表达式
合法的赋值:Runnable race = () -> System.out.println("Hello world !");
非法的赋值:String str = () -> {return "hello".toUpperCase();};
因为String类没有标注为函数式接口,不能接收Lambda表达式,会导致编译错误
四、Stream API
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
生成流
在 Java 8 中, 集合接口有两个方法来生成流:
stream() − 为集合创建串行流
parallelStream() − 为集合创建并行流
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
操作集合的方法 :Collection.stream(); 会返回一个Stream对象,通过这个Stream对象,以流的方式操作集合中的元素
1、filter
Stream<T> filter(Predicate<? super T> predicate);
对集合中的元素进行过滤,返回包含符合条件的元素流
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();
2、forEach
void forEach(Consumer<? super T> action);
遍历集合中的元素
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
strings.stream().forEach(string -> System.out.println(string));
strings.stream().forEach(System.out::println);//是上面的简写方式
3、map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
map 方法用于映射每个元素到对应的结果
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
案例:先对集合中元素进行操作,再对集合中年龄大于27岁的进行过滤,并遍历集合
List<Person> persons = new ArrayList<Person>(){
{//匿名类初始化参数
add(new Person("marvin", 18));
add(new Person("jerry", 31));
add(new Person("sally", 30));
add(new Person("max", 27));
add(new Person("jason", 32));
}
};
persons.stream()
//年龄大于27岁的对象返回,其他的设置为null
.map(e ->{
if(e.getAge()>27){
return e;
}
return null;
})
//过滤掉null的数据
.filter(e -> e!=null)
//将流转换为list集合
.collect(Collectors.toList())
//遍历集合中的对象
.forEach(System.out::println);
4、limit
Stream<T> limit(long maxSize);
limit 方法用于获取指定数量的流,返回参数maxSize所指定的个数的元素
//以下代码片段使用 limit 方法打印出随机 10 条数据
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
//获取前三个
List<String> s = Arrays.asList("1","2","3","4","5","6");
s.stream().limit(3).forEach(System.out::println);
5、sorted
Stream<T> sorted();
sorted 方法用于对流中的元素自然排序
Stream<T> sorted(Comparator<? super T> comparator);
根据参数指定的比较规则,对集合中元素排序
List<String> s = Arrays.asList("6","5","3","1","2","1");
s.stream().sorted().forEach(System.out::println);
案例:将集合中对象以年龄倒序排序
List<Person> persons = new ArrayList<Person>(){
{//匿名类初始化参数
add(new Person("marvin", 18));
add(new Person("jerry", 31));
add(new Person("sally", 30));
add(new Person("max", 27));
add(new Person("jason", 32));
}
};
persons.stream().sorted(Comparator.comparing(Person::getAge).reversed()).forEach(System.out::println);
6、Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
练习案例:
案例1:
对persons集合过滤,过滤条件为姓名字符串的长度大于3,接着按照姓名排序,再把集合中前三个Person对象的信息打印出来
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>(){
{//匿名类初始化参数
add(new Person("marvin", 18));
add(new Person("jerry", 31));
add(new Person("sally", 30));
add(new Person("max", 27));
add(new Person("jason", 32));
}
};
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()));
}
案例2:使用List流排序
package com.stream.demo;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class StreamListDemo {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student(1, "Mahesh", 12));
list.add(new Student(2, "Suresh", 15));
list.add(new Student(3, "Nilesh", 10));
System.out.println("---Natural Sorting by Name---");
List<Student> slist = list.stream().sorted().collect(Collectors.toList());
slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
System.out.println("---Natural Sorting by Name in reverse order---");
slist = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
System.out.println("---Sorting using Comparator by Age---");
slist = list.stream().sorted(Comparator.comparing(Student::getAge)).collect(Collectors.toList());
slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
System.out.println("---Sorting using Comparator by Age with reverse order---");
slist = list.stream().sorted(Comparator.comparing(Student::getAge).reversed()).collect(Collectors.toList());
slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
}
}
package com.stream.demo;
public class Student implements Comparable<Student> {
private int id;
private String name;
private int age;
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Student ob) {
return name.compareTo(ob.getName());
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
final Student std = (Student) obj;
if (this == std) {
return true;
} else {
return (this.name.equals(std.name) && (this.age == std.age));
}
}
@Override
public int hashCode() {
int hashno = 7;
hashno = 13 * hashno + (name == null ? 0 : name.hashCode());
return hashno;
}
}
执行结果
---Natural Sorting by Name---
Id:1, Name: Mahesh, Age:12
Id:3, Name: Nilesh, Age:10
Id:2, Name: Suresh, Age:15
---Natural Sorting by Name in reverse order---
Id:2, Name: Suresh, Age:15
Id:3, Name: Nilesh, Age:10
Id:1, Name: Mahesh, Age:12
---Sorting using Comparator by Age---
Id:3, Name: Nilesh, Age:10
Id:1, Name: Mahesh, Age:12
Id:2, Name: Suresh, Age:15
---Sorting using Comparator by Age with reverse order---
Id:2, Name: Suresh, Age:15
Id:1, Name: Mahesh, Age:12
Id:3, Name: Nilesh, Age:10
案例3:使用set流排序
package com.stream.demo;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
public class StreamSetDemo {
public static void main(String[] args) {
Set<Student> set = new HashSet<>();
set.add(new Student(1, "Mahesh", 12));
set.add(new Student(2, "Suresh", 15));
set.add(new Student(3, "Nilesh", 10));
System.out.println("---Natural Sorting by Name---");
System.out.println("---Natural Sorting by Name---");
set.stream().sorted().forEach(e -> System.out.println("Id:"
+ e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
System.out.println("---Natural Sorting by Name in reverse order---");
set.stream().sorted(Comparator.reverseOrder()).forEach(e -> System.out.println("Id:"
+ e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
System.out.println("---Sorting using Comparator by Age---");
set.stream().sorted(Comparator.comparing(Student::getAge))
.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
System.out.println("---Sorting using Comparator by Age in reverse order---");
set.stream().sorted(Comparator.comparing(Student::getAge).reversed())
.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
}
}
案例4:使用Map流排序
package com.stream.demo;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class StreamMapDemo {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(15, "Mahesh");
map.put(10, "Suresh");
map.put(30, "Nilesh");
System.out.println("---Sort by Map Value---");
map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue))
.forEach(e -> System.out.println("Key: "+ e.getKey() +", Value: "+ e.getValue()));
System.out.println("---Sort by Map Key---");System.out.println("---Sort by Map Key---");
map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println("Key: "+ e.getKey() +", Value: "+ e.getValue()));
}
}