文章目录
前言
Lambda表达式是jdk1.8新增的一个特性,学习它可以解决我们项目中的代码冗余问题、提高我们的开发效率、精简代码、学习它对以后钻研源码非常重要。
什么是Lambda?
1.Lambda概述
- Lambda表达式也被称为箭头函数、匿名函数、闭包
- Lambda表达式体现的是轻量级函数式编程思想
- ->符号是Lambda表达式核心操作符号,符号左边是操作参数,右边是操作表达式
2.为什么要使用Lambda?
首先它不是解决未知问题的新技术,它只是对现有的解决方案实现语意优化,使用的时候需要根据实际需求来考虑性能问题。
我们来看一个简单的例子对比一下Lambad和传统方法,很明显它把非业务部分都去除了,解决的代码的冗余。
package com.liyingdong;
public class Test1 {
public static void main(String[] args) {
// 传统模式的创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("传统线程:"+Thread.currentThread().getId());
}
}).start();
// jdk1.8新特性 Lambda创建线程
new Thread(()->{
System.out.println("Lambda线程:"+Thread.currentThread().getId());
}) .start();
}
}
Lambda表达式的基础知识
1.函数式接口
函数式接口,是java类型系统中的接口,函数式接口只能包含一个抽象方法,并且接口类要使用语义化检测注解@FunctionalInterface标记,可以有静态方法和默认方法,默认方法可以被从写。
有一个注意的点就是函数式接口它是继承与obj的,所以它可以定义从父类继承的方法,是可以通过函数式语法语义的检测的,例如String toString();
package com.liyingdong;
@FunctionalInterface
public interface IMessageFormat {
String format(String message, String format);
String toString();
static boolean verifyMessage(String msg) {
if (msg != null) {
return true;
}
return false;
}
}
2.Lambda表达式和函数式接口的关系
- 函数式接口,只包含一个操作方法
- Lambda表达式,只能操作一个方法
- java中的Lambda表达式,核心就是一个函数式接口的实现
3.jdk中常见的函数式接口
java.util.function提供了大量的函数式接口
Predicate 接收参数T对象,返回一个boolean类型结果
Consumer 接收参数T对象,没有返回值
Function 接收参数T对象,返回R对象
Supplier 不接受任何参数,直接通过get()获取指定类型的对象
UnaryOperator 接口参数T对象,执行业务处理后,返回更新后的T对象
BinaryOperator 接口接收两个T对象,执行业务处理后,返回一个T对象
// 三、JDK8 提供的常见函数式接口
Predicate<String> pre = (String username) -> {
return "admin".equals(username);
};
System.out.println(pre.test("ddm"));
System.out.println(pre.test("admin"));
Consumer<String> con = (String message) -> {
System.out.println("要发送的消息:" + message);
System.out.println("消息发送完成");
};
con.accept("好嗨哦!..");
con.accept("感觉人生已经到达了高潮.");
Function<String, Integer> fun = (String gender) -> {
return "male".equals(gender)?1:0;
};
System.out.println(fun.apply("male"));
System.out.println(fun.apply("female"));
Supplier<String> sup = () -> {
return UUID.randomUUID().toString();
};
System.out.println(sup.get());
System.out.println(sup.get());
System.out.println(sup.get());
UnaryOperator<String> uo = (String img)-> {
img += "[100x200]";
return img;
};
System.out.println(uo.apply("原图--"));
BinaryOperator<Integer> bo = (Integer i1, Integer i2) -> {
return i1 > i2? i1: i2;
};
System.out.println(bo.apply(12, 13));
输出:

4.Lambda表达式基本语法
分别为两种:
第一种:带参数的Lambda表达式
第二种:带返回值的Lambda表达式
声明: 就是和lambda表达式绑定的接口类型,
参数: 含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
操作符:->
执行代码块: 包含在一对大括号中,出现在操作符号的右侧, [接口声明] = (参数) -> {执行代码块};
我们定义几个内部接口来实现案例来实现Lambda表达式,前提是必须和接口进行绑定。
// 没有参数,没有返回值的lambda表达式绑定的接口
interface ILambda1{
void test();
}
// 带有参数,没有返回值的lambda表达式
interface ILambda2{
void test(String name, int age);
}
// 带有参数,带有返回值的lambda表达式
interface ILambda3 {
int test(int x, int y);
}
1.Lambad执行无参的接口方法。
ILambda1 i1 = () -> {
System.out.println("好嗨哦!");
System.out.println("感觉人生已经到达了高潮!");
};
如果只是单纯的打印的话没必要两行,直接一行解决。
ILambda1 i2 = () -> System.out.println("好震撼!");
i2.test();
2.执行带两个参数的Lambad方法
ILambda2 i21 = (String n, int a) -> {
System.out.println(n + "今年 " + a);
};
i21.test("我", 18);
3.lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中电参数进行推导。
ILambda2 i22 = (n, a) -> {
System.out.println(n + "今年" + a + "岁了.");
};
i22.test("我", 22);
4.lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。
如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。
ILambda3 i31 = (x, y) -> x + y;
System.out.println(i31.test(100, 200));
结果:

5.Lambda表达式类型检查
举例下面,基于函数式接口的定义jvm底层自动的实现了类型推导。
这里注明一下别的点Lambda是不适用于重载的。
package com.liyingdong;
import java.util.ArrayList;
import java.util.List;
public class Test3{
public static void test(MyInterface<String, List> inter) {
List<String> list = inter.strategy("hello", new ArrayList());
System.out.println(list);
}
// 传统方式
public static void main(String[] args) {
test(new MyInterface<String, List>() {
@Override
public List strategy(String s, List list) {
list.add(s);
return list;
}
});
// 表达式方式
test((x, y) -> {
y.add(x);
return y;
});
}
}
@FunctionalInterface
interface MyInterface<T, R> {
R strategy (T t, R r);
}
结果:

6.方法引用
这里的话方法用分为两种,静态引用、实例引用。
静态引用语法:对象::静态方法
实例引用语法:实例化对象::方法
package com.liyingdong;
import java.util.*;
class Person {
private String name;
private String gender;
private int age;
public Person () {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, String gender, int age) {
super();
this.name = name;
this.gender = gender;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", gender=" + gender + ", age=" + age + "]";
}
// 静态方法引用
public static int compareByAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
class PersonUtil {
// 实例方法引用
public int comprareByName(Person p1, Person p2) {
return p1.getName().hashCode() - p2.getName().hashCode();
}
}
interface IPerson {
Person getPerson(String name, String gender, int age);
}
public class Test {
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
list.add(new Person("海王", "男", 29));
list.add(new Person("海叼", "男", 16));
list.add(new Person("海班", "男", 20));
list.add(new Person("猴头菇", "女", 30));
// 匿名内部类实现
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
// lambda表达式实现
Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());
// 静态方法引用实现
Collections.sort(list, Person::compareByAge);
// 实例方法引用
PersonUtil pu = new PersonUtil();
Collections.sort(list, pu::comprareByName);
list.forEach(System.out::println);
IPerson p1 = Person::new;
Person person = p1.getPerson("东", "男", 18);
System.out.println(person);
}
}
输出:

Stram操作集合的常用方法
一般的话使用Lambda最常用的方式就是函数式接口配合Stram下面介绍一下 Stram操作集合的常用方法。
(1)stream的处理流程
-
1. 数据源 -
2. 数据转换 -
3. 获取结果
(2)获取Stream对象
从集合或者数组中获取[**]
- Collection.stream()
- accounts.stream()
- Arrays.stream(T t)
使用缓冲流BufferReader
BufferReader.lines()-> stream()
等等方式…
(3)中间操作API{intermediate}
操作结果是一个Stream,中间操作可以有一个或者多个连续的中间操作,需要注意的是,中间操作
只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。
中间操作:就是业务逻辑处理。
无状态:数据处理时,不受前置中间操作的影响。
- map
- filter
- peek
- parallel
- sequential
- unordered
有状态:数据处理时,受到前置中间操作的影响。
- distinct
- sorted
- limit
- skip
(4)终结操作|结束操作
需要注意:一个Stream对象,只能有一个结束操作,这个操作一旦发生,就会真实处理数据,生成对应的处理结果。
非短路操作:当前的Stream对象必须处理完集合中所有 数据,才能得到处理结果。
- forEach
- forEachOrdered
- toArray
- reduce
- collect
- min
- max
- count
- iterator
短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果。
- anyMatch
- allMatch
- noneMatch
- findFirst
- findAny
使用案例:
package com.liyingdong.test;
import java.util.*;
import java.util.stream.Stream;
public class Test7 {
public static void main(String[] args) {
// 1. 批量数据 -> Stream对象
// 多个数据
Stream stream = Stream.of("admin", "login", "ddd");
// 数组
String [] strArrays = new String[] {"sss", "dsda"};
Stream stream2 = Arrays.stream(strArrays);
// 列表
List<String> list = new ArrayList<>();
list.add("dd1");
list.add("dd2");
list.add("dd3");
list.add("dd4");
list.add("dd5");
Stream stream3 = list.stream();
// 集合
Set<String> set = new HashSet<>();
set.add("qq1");
set.add("qq2");
set.add("qq3");
Stream stream4 = set.stream();
// Map
Map<String, Integer> map = new HashMap<>();
map.put("tt", 1000);
map.put("ttt", 1200);
map.put("ttt", 1000);
Stream stream5 = map.entrySet().stream();
// 2. Stream对象对于基本数据类型的功能封装
// int / long / double
// IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println);
// IntStream.range(1, 5).forEach(System.out::println);
// IntStream.rangeClosed(1, 5).forEach(System.out::println);
// 3. Stream对象 --> 转换得到指定的数据类型
// 数组
// Object [] objx = stream.toArray(String[]::new);
// 字符串
// String str = stream.collect(Collectors.joining()).toString();
// System.out.println(str);
// 列表
// List<String> listx = (List<String>) stream.collect(Collectors.toList());
// System.out.println(listx);
// 集合
// Set<String> setx = (Set<String>) stream.collect(Collectors.toSet());
// System.out.println(setx);
// Map
// Map<String, String> mapx = (Map<String, String>) stream.collect(Collectors.toMap(x->x, y->"value:"+y));
// System.out.println(mapx);
// 4. Stream中常见的API操作
List<String> accountList = new ArrayList<>();
accountList.add("taijun");
accountList.add("haiwang");
accountList.add("liyingdong");
accountList.add("tangle");
accountList.add("liubiao");
accountList.add("ddd");
accountList.add("cc");
// map() 中间操作,map()方法接收一个Functional接口
// accountList = accountList.stream().map(x->"梁山好汉:" + x).collect(Collectors.toList());
// filter() 添加过滤条件,过滤符合条件的用户
// accountList = accountList.stream().filter(x-> x.length() > 5).collect(Collectors.toList());
// forEach 增强型循环
// accountList.forEach(x-> System.out.println("forEach->" + x));
// accountList.forEach(x-> System.out.println("forEach->" + x));
// accountList.forEach(x-> System.out.println("forEach->" + x));
// peek() 中间操作,迭代数据完成数据的依次处理过程
// accountList.stream()
// .peek(x -> System.out.println("peek 1: " + x))
// .peek(x -> System.out.println("peek 2:" + x))
// .forEach(System.out::println);
// accountList.forEach(System.out::println);
// Stream中对于数字运算的支持
List<Integer> intList = new ArrayList<>();
intList.add(20);
intList.add(19);
intList.add(7);
intList.add(8);
intList.add(86);
intList.add(11);
intList.add(3);
intList.add(20);
// skip() 中间操作,有状态,跳过部分数据
// intList.stream().skip(3).forEach(System.out::println);
// limit() 中间操作,有状态,限制输出数据量
// intList.stream().skip(3).limit(2).forEach(System.out::println);
// distinct() 中间操作,有状态,剔除重复的数据
// intList.stream().distinct().forEach(System.out::println);
// sorted() 中间操作,有状态,排序
// max() 获取最大值
Optional optional = intList.stream().max((x, y)-> x-y);
System.out.println(optional.get());
// min() 获取最小值
// reduce() 合并处理数据
Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);
System.out.println(optional2.get());
}
}
本文深入探讨Java 8引入的Lambda表达式及其在Stream API中的应用,讲解Lambda表达式的基本概念、语法和常见函数式接口,同时介绍Stream API的操作流程及常用方法,帮助读者提升代码效率和开发技能。

2476

被折叠的 条评论
为什么被折叠?



