Lambda表达式

Lambda 表达式的基础语法

  • Java8中引入了一个新的操作符 “->” 该操作符称为箭头操作符或 Lambda 操作符

箭头操作符将 Lambda 表达式拆分成两部分:

  • 左侧:Lambda 表达式的参数列表

  • 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体

语法格式

  • 语法格式一:无参数,无返回值
1
() -> System.out.println("Hello Lambda!");
  • 语法格式二:有一个参数,并且无返回值
1
(x) -> System.out.println(x)
  • 语法格式三:若只有一个参数,小括号可以省略不写
1
x -> System.out.println(x)
  • 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
1
2
3
4
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
  • 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
1
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
  • 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
1
(Integer x, Integer y) -> Integer.compare(x, y);

上联:左右遇一括号省
下联:左侧推断类型省
横批:能省则省

Lambda 需要“函数式接口”的支持

  • 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
  • 可以使用注解 @FunctionalInterface 修饰可以检查是否是函数式接口

Java8 内置的四大核心函数式接口

1
2
Consumer<T> : 消费型接口
void accept(T t);
1
2
Supplier<T> : 供给型接口
T get();
1
2
Function<T, R> : 函数型接口
R apply(T t);
1
2
Predicate<T> : 断言型接口
boolean test(T t);

方法引用

  • 若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用

  • (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)

三种语法格式

  • 对象的引用 :: 实例方法名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  ## 原lambda表达式写法
Consumer<String> con = (x)->System.out.println(x);
## 改写
PrintStream ps = System.out;
Consumer<String> con = (x)->ps.println(x);

##其中,println方法已经由System.out对象实现
PrintStream ps = System.out; //创建对象 #ps是System.out对象的引用
Consumer<String> con = ps::println; //对象的引用 :: 实例方法名

##再次简写
Consumer<String> con = System.out::println;

con.accept("abcd")


Employee emp = new Employee();
Supplier<String> sup = () -> emp.getName(); //✅
Supplier<String> sup = () -> emp.getAge(); //❌,返回类型不匹一致

## getAge()已经由emp对象实现,可以简写
Supplier<Integer> sup = emp::getAge();
  • 类名 :: 静态方法名
1
2
3
4
5
6
7
8
9
10
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

## 其中,compare 在 Integer 类中为静态方法
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
## Integer 中 compare方法两参一返
## Comparator 中,int compare(T o1, T o2),两参一返
## 简写
Comparator<Integer> com = (x, y) -> Integer::compare;
  • 类名 :: 实例方法名
1
2
3
4
5
6
7
8
9
public interface BiPredicate<T, U> {
boolean test(T t, U u);
}
## 原lambda表达式
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
## equals()在String中已实现,
## 第一个参数是方法的调用者,第二个参数是方法的参数,可以简写
## 在x.equals(y)中,x 是equal()的调用者,y是equal()的参数
BiPredicate<String, String> bp = String::equals

⚠️注意

  • ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!

  • ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName

构造器引用

语法格式

  • 类名 :: new
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Supplier<T> {
T get(); //无参有返
}
## 原lambda
Supplier<Employee> sup = () -> new Employee(); //返回类型为Employee
## 简写
Supplier<Employee> sup = Employee::new; //自动匹配无参构造器
//sup.get();
public interface Function<T, R> {
R apply(T t); //一参一返
}
## 原lambda
Function<Integer,Employee> fe = (x)-> new Employee(x)
## 简写
Function<Integer,Employee> fe = Employee::new;//自动匹配一个参数构造器
//fe.apply(11);
//BiFunction 两参一返
BiFunction<String, Integer, Employee> fun2 = Employee::new; //此时Employee必须要有两个参数的构造器

⚠️注意

构造器的参数列表,需要与函数式接口中参数列表保持一致!

数组引用

语法格式

  • 类型[ ] :: new
1
2
3
4
5
6
7
8
9
  ## 原lambda
Function<Integer, String[]> fc = (x) -> new String[x];
String[] strs = fc.apply(10);
System.out.println(strs.length); //String数组长度为10
## 简写
Function<Integer, Employee[]> fc = Employee[] :: new;
Employee[] emps = fc.apply(20);
System.out.println(emps.length); //长度为20

Stream

  • Stream 自己不会存储元素。
  • Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream API 的操作步骤:

  • 创建 Stream
  • 中间操作
  • 终止操作(终端操作)

创建 Stream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//1. Collection 提供了两个方法  stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

//2. 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);

//3. 通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);

//4. 创建无限流
//迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);

//生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);

中间操作

筛选与切片

  • 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

filter:接收Lambda,从流中排除某些元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//内部迭代:由Stream API完成迭代
public void test(){
List<Person> people = Arrays.asList(
new Person("张三", 18, 9999.99),
new Person("李四", 58, 5555.55),
new Person("王五", 26, 3333.33),
new Person("赵六", 36, 6666.66),
new Person("田七", 12, 8888.88)
);

//中间操作 ,没有终止操作,所有中间操作不会执行
Stream<Person> stream = people.stream().filter(person -> {
System.out.println("中间操作");
return person.getAge() > 5;
});
//终止操作
stream.forEach(System.out::println);
}

limit:截断流,使其元素不超过给定数量

​ 短路:满足limit限制后不再执行

1
2
3
4
people.stream()
.filter(person -> person.getScore() > 5000)
.limit(2) //中间操作⬆️
.forEach(System.out::println); //终止操作

skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补

1
2
3
4
people.stream()
.filter(person -> person.getScore() > 5000)
.skip(2)
.forEach(System.out::println);

distinct:筛选,通过流所生成元素的hashcode() 和equals() 去除重复元素,

​ 要想实现,需重写这两个方法

1
2
3
people.stream()
.distinct()
.forEach(System.out::println);

映射

map:接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

1
2
3
4
5
@Test
public void test() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
}

flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class test {
@Test
public void test() {
List<String> list = Arrays.asList("aaa", "bbb");
list.stream().flatMap(test::filterCharacter).forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}

排序

sorted()——自然排序(Comparable)

sorted(Comparator com)——定制排序(Comparator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void test(){
//自然排序
List<String> list = Arrays.asList("aaa", "eee", "ddd", "bbb");
list.stream().sorted().forEach(System.out::println);

//定制排序
List<Person> list1 = Arrays.asList(
new Person("张三", 18, 2000.0),
new Person("李四", 18, 5000.0),
new Person("王五", 45, 8700.0),
new Person("赵六", 42, 4200.0),
new Person("陈七", 56, 13100.0)
);
//如果年龄一样则按照姓名排序
list1.stream().sorted((p1,p2) -> {
if (p1.getAge().equals(p2.getAge())){
return p1.getName().compareTo(p2.getName());
}else {
return p1.getAge().compareTo(p2.getAge());
}
}).forEach(System.out::println);
}

终止操作

匹配与查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Test
public void test(){
List<Person> list = Arrays.asList(
new Person("张三", 18, 2000.0, Person.Status.BUSY),
new Person("李四", 18, 5000.0,Person.Status.FREE),
new Person("王五", 45, 8700.0,Person.Status.VOCATION),
new Person("赵六", 42, 4200.0,Person.Status.BUSY),
new Person("陈七", 56, 13100.0,Person.Status.BUSY)
);
//allMatch 检查是否匹配所有元素,返回值为Boolean类型
boolean b0 = list.stream().allMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b0); // false

//anyMatch 检查是否匹配至少一个元素,返回值为Boolean类型
boolean b1 = list.stream().anyMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b1); // true

//noneMatch 检查是否没有匹配所有元素,返回值为Boolean类型
boolean b2 = list.stream().noneMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b2); // false

//findFirst 返回第一个元素
//Optional 防止空指针异常的类型,如果first为null,可以使用.orelse()方法指定一个不为空的对象
Optional<Person> op1 = list.stream()
.sorted((e1, e2) -> Double.compare(e1.getSale(), e2.getSale())).findFirst();
System.out.println(op1.get()); // Person(name=张三, age=18, sale=2000.0, status=BUSY)

//findAny 返回当前流中的任意元素
//parallelStream 并行流,多个进程同时去进行filter、findAny,谁先找到算谁的
Optional<Person> op2 = list.parallelStream().filter(e -> e.getStatus().equals(Person.Status.FREE)).findAny();
System.out.println(op2.get()); // Person(name=李四, age=18, sale=5000.0, status=FREE)

//count 返回流中元素的总个数
long count = list.stream().count();
System.out.println(count); // 5

//max 返回流中的最大值
Optional<Person> max = list.stream().max((e1, e2) -> Double.compare(e1.getSale(), e2.getSale()));
System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY)


//min 返回流中的最小值
//返回list中的最小工资数
System.out.println(list.stream().map(Person::getSale).min(Double::compare).get()); // 2000.0
}

归约

reduce(T identity,BinaryOperator) / reduce(BinarOperator):可以将流中元素反复结合起来,得到一个值。

​ map和reduce的连接通常被成为map-reduce模式,因Google用它来进行网络搜索而出名

1
2
3
4
5
6
7
8
9
10
11
@Test
public void test(){
List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//从0开始作为起始值,0作为x,先取y=1,执行x+y=1,再将x作为1,y取流中下一个元素2,一直执行下去
Integer sum = list1.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);

//上面那个返回值是Integer,是因为有起始值,不可能为空,而这条语句没有起始值,有可能为空!
Optional<Double> reduce = list.stream().map(Person::getSale).reduce(Double::sum);
System.out.println(reduce.get());
}

收集

collect:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。

​ Collectors类中提供了很多静态方法,能方便的创建常见收集器实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
public void test(){
List<String> nameList = list.stream().map(Person::getName).collect(Collectors.toList());
//放入set可以进行去重
Set<String> collect = list.stream().map(Person::getName).collect(Collectors.toSet());

//要想放入我们指定的收集器中,可以采取以下方式
// Collectors.toCollection( xxx ::new) xxx为我们想要放入的集合类型
// 例如:想将list中的人员name收集放入一个LinkedList中
LinkedList<String> collect1 = list.stream().map(Person::getName).collect(Collectors.toCollection(LinkedList::new));

//总数
Long count = list.stream().collect(Collectors.counting());
System.out.println(count); // 5

//平均值
Double avg = list.stream().collect(Collectors.averagingDouble(Person::getSale));
System.out.println(avg); // 6600.0

//总和
Double sum = list.stream().collect(Collectors.summingDouble(Person::getSale));
System.out.println(sum); // 33000.0

//最大值:最大salary的员工信息
Optional<Person> max = list.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSale(), e2.getSale())));
System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY)

//最小值:最小的salary
Optional<Double> min = list.stream().map(Person::getSale).collect(Collectors.minBy(Double::compare));
System.out.println(min.get()); // 2000.0
}

分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   //简单分组
Map<Person.Status, List<Person>> map = list.stream().collect(Collectors.groupingBy(Person::getStatus));
System.out.println(map);
// {FREE=[Person(name=张三, age=18, sale=5000.0, status=FREE)],
// BUSY=[Person(name=张三, age=18, sale=2000.0, status=BUSY), Person(name=赵六, age=42, sale=4200.0, status=BUSY), Person(name=陈七, age=56, sale=13100.0, status=BUSY)],
// VOCATION=[Person(name=王五, age=45, sale=8700.0, status=VOCATION)]}

//多级分组
Map<Person.Status, Map<String, List<Person>>> map2 = list.stream().collect(Collectors.groupingBy(Person::getStatus, Collectors.groupingBy(e -> {
if (e.getAge() <= 35) {
return "青年";
} else if (e.getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
//{FREE={青年=[Person(name=张三, age=18, sale=5000.0, status=FREE)]},
// BUSY={青年=[Person(name=张三, age=18, sale=2000.0, status=BUSY)], 老年=[Person(name=陈七, age=56, sale=13100.0, status=BUSY)], 中年=[Person(name=赵六, age=42, sale=4200.0, status=BUSY)]},
// VOCATION={中年=[Person(name=王五, age=45, sale=8700.0, status=VOCATION)]}}
System.out.println(map2);
}

分区

1
2
3
4
    Map<Boolean, List<Person>> map = list.stream().collect(Collectors.partitioningBy(e -> e.getSale() > 9000.0));
System.out.println(map);
//{false=[Person(name=张三, age=18, sale=2000.0, status=BUSY), Person(name=张三, age=18, sale=5000.0, status=FREE), Person(name=王五, age=45, sale=8700.0, status=VOCATION), Person(name=赵六, age=42, sale=4200.0, status=BUSY)],
// true=[Person(name=陈七, age=56, sale=13100.0, status=BUSY)]}

其他

1
2
3
4
5
6
7
8
9
10
//对数据进行统计
DoubleSummaryStatistics dss = list.stream().collect(Collectors.summarizingDouble(Person::getSale));
System.out.println(dss.getAverage()); // 6600.0
System.out.println(dss.getCount()); // 5
System.out.println(dss.getMax()); // 13100.0

//对数据进行连接
//joining(",", "**", "=="),中间加, 前面加**,后面加==
String str = list.stream().map(Person::getName).collect(Collectors.joining(",", "**", "=="));
System.out.println(str); // **张三,张三,王五,赵六,陈七==