文章目录
Lambda表达式语法及应用
之前在Mybatis-plus中一直有用到Lambda表达式的用法,就找了一些教程具体学习了一下,做个记录。
Lambda简单比较
传统模式下创建新线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threading..." + Thread.currentThread().getId());
}
}).start();
Lambda表达式创建新线程
new Thread(() -> {
System.out.println("lambda threading..." + Thread.currentThread().getId());
});
函数式接口
Lambda的核心是就是一个函数式接口 ,函数式接口是Java类型系统中的特殊接口,语义检测注解:@FunctionalInterface,Lambda只能操作一个方法
案例
首先创建一个函数式接口
@FunctionalInterface
public interface UserCredential {
/**
* 校验用户信息
*
* @param username
* @return
*/
String verifyUser(String username);
default String getCredential(String username) {
if ("admin".equals(username)) {
return "default 系统管理员";
} else if ("manager".equals(username)) {
return "default 用户管理员";
} else {
return "default 普通会员";
}
}
}
接口实现类
public class UserCredentialImpl implements UserCredential {
/**
* 校验用户信息
*
* @param username
* @return
*/
@Override
public String verifyUser(String username) {
if ("admin".equals(username)) {
return "系统管理员";
} else if ("manager".equals(username)) {
return "用户管理员";
} else {
return "普通会员";
}
}
}
调用测试
public static void main(String[] args) {
// 简单实现
UserCredential user = new UserCredentialImpl();
System.out.println(user.verifyUser("admin"));
System.out.println(user.getCredential("manager"));
// 匿名内部类,实现接口的抽象方法
UserCredential ic = new UserCredential() {
@Override
public String verifyUser(String username) {
return "admin".equals(username) ? "管理员" : "会员";
}
};
System.out.println(ic.verifyUser("manager"));
System.out.println(ic.verifyUser("admin"));
// lambda 表达式,针对函数式接口的简单实现
UserCredential ic2 = (String username) -> {
return "admin".equals(username) ? "lambda管理员" : "lambda会员";
};
System.out.println(ic2.verifyUser("admin"));
System.out.println(ic2.verifyUser("manager"));
}
常见的函数式接口
接收参数T,返回boolean
Predicate<String> pre = (String username) -> {
return "admin".equals(username);
};
System.out.println(pre.test("admin"));
System.out.println(pre.test("manager"));
接收参数T,没有返回值
Consumer<String> con = (String message) -> {
System.out.println("发送的消息是:" + message);
System.out.println("发送完毕");
};
con.accept("nice");
con.accept("CseaNB");
接收参数T对象,返回R对象
Function<String, Integer> fun = (String gender) -> {
return "male".equals(gender) ? 1 : 0;
};
System.out.println(fun.apply("male"));
System.out.println(fun.apply("female"));
不接收任何参数,直接通过get()获取指的类型对象
Supplier<String> sup = () -> {
return UUID.randomUUID().toString();
};
System.out.println(sup.get());
System.out.println(sup.get());
System.out.println(sup.get());
接收参数对象T,返回结果对象T
UnaryOperator<String> uo = (String img) -> {
img += "[100*200]";
return img;
};
System.out.println(uo.apply("原图--"));
接收两个T对象,返回一个T对象结果
BinaryOperator<String> bo = (String firstname, String lastname) -> {
firstname += "Csea";
lastname += "NB";
return firstname + lastname;
};
System.out.println(bo.apply("first", "last"));
Lambda表达式的基本语法
声明:和Lambda表达式绑定的接口类型
参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致
操作符:->
执行代码块:包含在一对大括号中,出现在操作符的右侧
接口声明:(参数) -> {执行代码块}
案例
没有参数,没有返回值的Lambda表达式绑定的接口
interface ILambda {
void test();
}
ILambda iLambda1 = () -> {
System.out.println("Csea");
System.out.println("NB");
};
iLambda1.test();
带有参数,没有返回值的Lambda表达式
interface ILambda2 {
void test(String name, int age);
}
// 如果执行代码块只有一行,可以省略{}
ILambda iLambda2 = () -> System.out.println("iLambda2");
iLambda2.test();
ILambda2 iLambda21 = (String n, int a) -> {
System.out.println("name:" + n + " age " + a);
};
iLambda21.test("Csea", 22);
// 这里Lambda会自动识别参数类型
ILambda2 iLambda22 = (n, a) -> {
System.out.println("name: " + n + " age " + a);
};
iLambda22.test("Csea", 22);
带有参数,带有返回值的Lambda表达式
interface ILambda3 {
Integer test(Integer x, Integer y);
}
ILambda3 iLambda3 = (x, y) -> {
return x + y;
};
System.out.println(iLambda3.test(1, 2));
// 如果只有一行代码块,还可以省略return
ILambda3 iLambda31 = (x, y) -> x + y;
System.out.println(iLambda31.test(3, 3));
小结:
- lambda表达式,必须和接口绑定;
- Lambda表达式的参数,可以附带0个到n个参数,括号在的参数类型可以不用指定,JVM在运行时,会自动根据绑定的抽象方法中的自动推导;
- lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回;
- 若果添加了大括号,或者有多行代码,必须通过return关键词返回结果。
Lambda访问变量
public class App2 {
String s1 = "全局变量";
public void testInnerClass() {
String s2 = "局部变量";
new Thread(new Runnable() {
String s3 = "内部变量";
@Override
public void run() {
System.out.println(s1);
System.out.println(s2);//不能对局部变量进行修改(final)
System.out.println(s3);
System.out.println(this.s3);
}
}).start();
}
public void lambdatest() {
String s2 = "局部lambda";
new Thread(() -> {
String s3 = "内部lambda";
System.out.println(this.s1); //this关键词,表示的是所属方法所在的对象
System.out.println(s2);//不能对局部变量进行修改(final)
System.out.println(s3);
}).start();
}
public static void main(String[] args) {
App2 app2 = new App2();
app2.testInnerClass();
app2.lambdatest();
// lambda变量的访问操作优化了匿名类内部的this关键字,不再单独建立对象作用域
// 表达式本身就是所属类型对象的一部分,在语法语义上更加简洁
}
}
Lambda类型检查
public class App3 {
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;
});
/**
* (x,y) -> {...} --> test(param) --> param==MyInterface --> lambda表达式 -> MyInterface类型
* 这个就是对于lambda表达式的类型检查,MyInterface接口就是lambda表达式的目标类型(target typing)
*
*(x,y) -> {...} --> MyInterface.strategy(T t,R r) --> MyInterface<String,List> inter
* --> T == String R==List --> lambda --> (x,y) == strategy(T t,R r) --> x == T == String y==String==List
* lambda表达式参数的类型检查
*/
}
@FunctionalInterface
interface MyInterface<T, R> {
R strategy(T t, R r);
}
}
Lambda方法重载
public class App4 {
interface Param1 {
void outInfo(String info);
}
interface Param2 {
void outInfo(String info);
}
public void lambdaMethod(Param1 param1) {
param1.outInfo("Hello Csea");
}
public void lambdaMethod(Param2 param2) {
param2.outInfo("Hi Csea");
}
public static void main(String[] args) {
App4 app4 = new App4();
app4.lambdaMethod(new Param1() {
@Override
public void outInfo(String info) {
System.out.println(info);
}
});
/**
* lambda存在类型检查 -> 自动推到lambda表达式目标类型
* lambdaMethod() -> 方法 -> 重载方法
* -> Param1 函数式接口
* -> Param2 函数式接口
* 调用方法 -> 传递Lambda表达式 -> 自动推导
* -> Param1 | Param2
*/
/**
* 这里抛红是因为 他对两个lambda表达式去进行了匹配
* 在某情况下限制了传统语法结构中功能操作
* 如果在出现方法重载的类型中,参数都是函数式接口的情况,请使用匿名内部类的实现替代lambda
*/
// app4.lambdaMethod((String info) -> {
// System.out.println(info);
// });
}
}
Lambda表达式在集合中的运用
public class Test {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("tom", "男", 16));
personList.add(new Person("jerry", "男", 18));
personList.add(new Person("mary", "男", 24));
personList.add(new Person("tony", "男", 30));
personList.add(new Person("csea", "男", 22));
personList.add(new Person("spack", "男", 50));
// 匿名内部类实现方式
Collections.sort(personList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println(personList);
// lambda表达式 实现
Collections.sort(personList, (p1, p2) -> p1.getAge() - p2.getAge());
System.out.println(personList);
// 静态方法引用
Collections.sort(personList, Person::compareByAge);
System.out.println(personList);
// 实例方法引用方式
PersonUtil personUti = new PersonUtil();
Collections.sort(personList, personUti::compareByName);
System.out.println(personList);
// 构造方法引用:函数式接口使用
IPerson ip = Person::new;
Person person = ip.initPerson("tom", "男", 22);
System.out.println(person);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private String gender;
private int age;
public static int compareByAge(Person person1, Person person2) {
return person1.getAge() - person2.getAge();
}
}
class PersonUtil {
public int compareByName(Person p1, Person p2) {
return p1.getName().hashCode() - p2.getName().hashCode();
}
}
interface IPerson {
Person initPerson(String name, String gender, int age);
}
引入Stream API与其他方式对比
public class Test2 {
public static void main(String[] args) {
// 存储账号列表
List<String> accounts = new ArrayList<>();
accounts.add("tom");
accounts.add("jerry");
accounts.add("csea");
accounts.add("shuke");
accounts.add("spack");
// 长度大于5才是有效账号
for (String account : accounts) {
if (account.length() >= 5) {
System.out.println("有效账号" + account);
}
}
// 迭代器操作
Iterator<String> it = accounts.iterator();
while (it.hasNext()) {
String account = it.next();
if (account.length() >= 5) {
System.out.println("有效账号" + account);
}
}
// steam结合lambda表达式 进行处理
// 先获取到accounts列表,然后通过 过滤 拿到长度 >= 5的数据,再把它转换成一个list
List validAccounts = accounts.stream().filter(s -> s.length() >= 5).collect(Collectors.toList());
System.out.println(validAccounts);
}
}
Stream概述
Stream 是一个聚合操作
stream处理流程:
- 数据源
- 数据转换
- 获取结果
获取stream对象
-
1.从集合或者数组中获取
Collection.stream()
Collection.parallelStream()
Array.stream(T t)
-
- BufferReader
BufferReder.lines() -> stream()
-
- 静态工厂
java.util.stream.IntStream.range…
java.nio.file.Files.walk()…
-
- 自定构建
java.util.Spliterator
-
- 更多方式…
Random.ints()
Pattern.splitAsStream()…
中间操作API{intermediate}
操作结果是一个Stream,中间操作可以有一个或多个连续的中间操作,需要注意的是,中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。
中间操作过程:
-
无状态,数据处理时,不受前置中间操作的影响
map/filter/peek/parallel/sequential/unordered
-
有状态:数据处理时,受前置中间操作的影响
distinct/sorted/limit/skip
终结操作,结束操作{Terminal}
需要注意:一个Stream对象,只有一个Terminal操作,这个操作一旦发生,就会真实处理数据,生成对应的处理结果(不可逆)
终结操作:
-
非短路操作,当前的Stream对象必须处理完集合中所有的数据,才能得到处理结果
forEach/forEachOrdered/toArray/reduce/collect/min/max/cout/iterator
-
短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果
anyMatch/allMatch/noneMatch/findFirst/findAny
对于无限大的Stream -> 有限大的Stream
Stream操作集合
集合的简单操作
public class Test1 {
public static void main(String[] args) {
// 批量数据 转换 stream对象
Stream stream1 = Stream.of("admin", "tom", "damu");
// 数组
String[] strArray = new String[]{"xxxx", "yyyy"};
Stream stream2 = Arrays.stream(strArray);
// 列表
List<String> list = new ArrayList<>();
list.add("苹果");
list.add("西瓜");
list.add("香蕉");
list.add("菠萝");
list.add("樱桃");
Stream stream3 = list.stream();
// 集合
Set<String> set = new HashSet<>();
set.add("华硕");
set.add("Apple");
set.add("联想");
Stream stream4 = set.stream();
// Map
Map<String, Integer> map = new HashMap<>();
map.put("tom", 100);
map.put("jerry", 200);
map.put("csea", 10000);
Stream stream5 = map.entrySet().stream();
// Stream 对象 对于基本数据类型的功能封装
// int long double
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 5).forEach(System.out::println);// 包含起始数据,不包含结束数据
IntStream.rangeClosed(1, 5).forEach(System.out::println);// 包含起始数据,包含结束数据
// Stream对象 --> 转换得到 指定的数据类型,stream 一次终端操作之后就不能再次操作了
// 数组
Object[] objx = stream1.toArray(String[]::new);
// 字符串
String str = stream1.collect(Collectors.joining()).toString();
// 列表
List<String> listx = (List<String>) stream1.collect(Collectors.toList());
// 结合
Set<String> setx = (Set<String>) stream1.collect(Collectors.toSet());
// map
Map<String, Object> mapx = (Map<String, Object>) stream1.collect(Collectors.toMap(x -> x, y -> "value:" + y));
}
}
常见Stream API操作
public class Test1 {
public static void main(String[] args) {
// stream中常见的API操作
List<String> accountList = new ArrayList<>();
accountList.add("xxxx");
accountList.add("yyyyyy");
accountList.add("zzzz");
accountList.add("aaaaaa");
// map中间操作,map()方法接收一个Function接口
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));
// peek() 中间操作,迭代数据完成数据的依次处理过程
accountList.stream()
.peek(x -> System.out.println("peek1 :" + x))
.peek(x -> System.out.println("peek 2:" + x))
.forEach(System.out::println);
accountList.forEach(System.out::println);
// stream对于数字运算的支持
List<Integer> initList = new ArrayList<>();
initList.add(1);
initList.add(2);
initList.add(22);
initList.add(88);
initList.add(22);
// skip() 中间操作,有状态,跳过部分数据
initList.stream().skip(3).forEach(System.out::println);
// limit() 中间操作,有状态,限制输出数据量
initList.stream().skip(3).limit(2).forEach(System.out::println);
// distinct()中间操作,有状态,剔除重复的数据
initList.stream().distinct().forEach(System.out::println);
// sorted中间操作,有状态,排序
initList.stream().sorted();
// max() 获取最大值
initList.stream().max((x, y) -> x - y);
// min() 获取最小值
// reduce()合并处理数据
Optional optional = initList.stream().reduce((sum, y) -> sum + y);
}
}
性能对比——简单数据处理
因为不同的机器跑出来的数据也不同,这里我就不贴我的运行时间了,如果感兴趣就把代码copy一下到自己的机器上运行。
public class Test2 {
public static void main(String[] args) {
Random random = new Random();
List<Integer> integerList = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
integerList.add(random.nextInt(Integer.MAX_VALUE));
}
// 1.stream
testStream(integerList);
// 2.parallelStream
testParallelStream(integerList);
// 3.普通for
testForLoop(integerList);
// 4.增强型for
testStrongForLoop(integerList);
// 5. 迭代器
testInterator(integerList);
}
public static void testStream(List<Integer> list) {
long start = System.currentTimeMillis();
Optional optional = list.stream().max(Integer::compare);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testStream:" + (end - start) + "ms");
}
public static void testParallelStream(List<Integer> list) {
long start = System.currentTimeMillis();
Optional optional = list.parallelStream().max(Integer::compare);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testParallelStream:" + (end - start) + "ms");
}
public static void testForLoop(List<Integer> list) {
long start = System.currentTimeMillis();
int max = Integer.MIN_VALUE;
for (int i = 0; i < list.size(); i++) {
int current = list.get(i);
if (current > max) {
max = current;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testForLoop:" + (end - start) + "ms");
}
public static void testStrongForLoop(List<Integer> list) {
long start = System.currentTimeMillis();
int max = Integer.MIN_VALUE;
for (Integer integer : list) {
if (integer > max) {
max = integer;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testStrongForLoop:" + (end - start) + "ms");
}
public static void testInterator(List<Integer> list) {
long start = System.currentTimeMillis();
Iterator<Integer> it = list.iterator();
int max = it.next();
while (it.hasNext()) {
int current = it.next();
if (current > max) {
max = current;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testInterator:" + (end - start) + "ms");
}
}
性能对比——复杂数据:对象
public class Test3 {
public static void main(String[] args) {
Random random = new Random();
// 复杂数据类型:对象
List<Product> productList = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
productList.add(new Product("pro" + i, i, random.nextInt(Integer.MAX_VALUE)));
}
// 1.stream
testStream(productList);
// 2.parallelStream
testParallelStream(productList);
// 3.普通for
testForLoop(productList);
// 4.增强型for
testStrongForLoop(productList);
// 5. 迭代器
testInterator(productList);
}
public static void testStream(List<Product> list) {
long start = System.currentTimeMillis();
Optional optional = list.stream().max((p1, p2) -> p1.hot - p2.hot);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testStream:" + (end - start) + "ms");
}
public static void testParallelStream(List<Product> list) {
long start = System.currentTimeMillis();
Optional optional = list.parallelStream().max((p1, p2) -> p1.hot - p2.hot);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testParallelStream:" + (end - start) + "ms");
}
public static void testForLoop(List<Product> list) {
long start = System.currentTimeMillis();
Product max = list.get(0);
for (int i = 1; i < list.size(); i++) {
if (list.get(i).hot > max.hot) {
max = list.get(i);
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testForLoop:" + (end - start) + "ms");
}
public static void testStrongForLoop(List<Product> list) {
long start = System.currentTimeMillis();
Product max = list.get(0);
for (Product product : list) {
if (product.hot > max.hot) {
max = product;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testStrongForLoop:" + (end - start) + "ms");
}
public static void testInterator(List<Product> list) {
long start = System.currentTimeMillis();
Iterator<Product> it = list.iterator();
Product max = it.next();
while (it.hasNext()) {
Product current = it.next();
if (current.hot > max.hot) {
max = current;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testInterator:" + (end - start) + "ms");
}
@AllArgsConstructor
static
class Product {
String name;
Integer stock;
Integer hot;
}
}
小结:
- 对于简单数据的处理,可以通过外部迭代来操作;
- 如果对于性能有一定的要求,可以使用并行stream来操作;
- 对于复杂对象处理的操作,stream的串行操作和普通的迭代相差无几,甚至超过普通迭代,完全可以简洁的stream来代替普通迭代。
线程安全
public class Test4 {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list1.add(i);
}
// 串行stream
List<Integer> list2 = new ArrayList<>();
list1.stream().forEach(x -> list2.add(x));
System.out.println(list1.size());
System.out.println(list2.size());
// 并行stream
List<Integer> list3 = new ArrayList<>();
list1.parallelStream().forEach(x -> list3.add(x));
System.out.println(list3.size());
/**
* 并行会数据丢失,因为当前操作的线程集合并不是安全的,所以多个线程访问共享数据出现了冲突,
* 这个在redis中也有存在(特别是秒杀系统),可以添加redis分布式锁解决
* 在官方的文档中说明:由于并行stream中使用Collections本身就不是线程安全,所以会由于多线程情况下导致数据不一致
* Collections有提供自身的线程同步块,但是使用这些线程同步块可能存在线程竞争的问题
* 如果想避免线程竞争的问题 operations和parallelStream能够让我们得到一个 在非线程安全的情况下 数据处理过程
* 这个前提是 我们在操作的过程中,对于非线程安全中的数据不能修改
*/
//解决list.parallelStream()并行流线程安全的方式:
// 自定义编码,添加线程锁的方式,或者stream API中的线程安全的终端安全操作完成执行过程
List<Integer> list4 = list1.parallelStream().collect(Collectors.toList());
}
}
总结
个人觉得Lambda在代码上能一定的帮助我们进行优化,减少代码量,但是有些场景并不适合采用Lambda表达式,毕竟代码还是要一直维护的,Lambda表达式简化了代码表达,但是可读性也降低了。