lambda
读懂lambda表达式 简化过程
public class Lambda1 {
interface Printer{
void print(String var);
}
public void pringSomething(String var ,Printer printer){
printer.print(var);
}
public static void main(String[] args) {
Lambda1 lambda1 = new Lambda1();
String some = "qwer";
Printer printer = new Printer() {
@Override
public void print(String var) {
System.out.println(var);
}
};
lambda1.pringSomething(some,printer);
// 使用lambda表达式 一步一步简化的过程
// 简化1
Printer printer1 = (String var)-> //() 括号表示参数 {} 大括号里面的是函数体
{
System.out.println(var);
};
// 简化2 自动识别参数类型
Printer printer2 = (var)->
{
System.out.println(var);
};
// 简化3 就一个参数 可以把括号去掉
Printer printer3 = var ->
{
System.out.println(var);
};
// 简化4 函数体就一句 可以省略大括号
Printer printer4 = var -> System.out.println(var);
// 简化5 合起来一起写
lambda1.pringSomething(some,var -> System.out.println(var));
// 简化6 无参的构造函数编写 () -> System.out.println(var));
}
}
lambda表达式处理过程
从图中我们可以看到主要分成三个步骤:
第一步是数组 集合 io文件转化成流的过程
第二步是流的一些操作 中间包含有状态 和 无状态的处理过程
第三步经过处理后的数据 可以对起结果进行求和 变成集合的的一些流操作
第一步转化成流
list set转化成流
//list转换成流
List<String> stringList = Arrays.asList("tom", "anny", "ming", "uzi","tam","tang");
List<String> filterList = stringList.stream() //转化成流
.filter(s -> s.startsWith("t")) // 以t开头 filter是留下来的数据进行处理而不是过滤掉这些数据
.map(String::toUpperCase) // 转变成大写
.sorted() // 排序
.limit(2) // 取几个
.collect(Collectors.toList()); // 转化成集合
System.out.println(filterList);
// set转化成流
Set<String> set = new HashSet<>(stringList);
Stream<String> stream = set.stream(); //直接转化成流
数组转化成流
// 数组转化成流
String[] arrays = {"tom", "anny", "ming", "uzi","tam","tang"};
Stream<String> arrays1 = Stream.of(arrays); //这样就可以把数组转化成流 接下来操作就和list一样了
文件转化成流
// 文本文件每一行转化成流
Stream<String> lines = Files.lines(Paths.get("a.txt"));
第二步对流就行处理
分成无状态和有状态 无状态:状态一般是 多人多用户多线程多次访问的公共数据 无状态就是不会变 有状态就是会变 想session 每次用户访问的session都是一样的
filter管道数据处理(无状态)
主要是谓语的处理 过滤数据
public class Lambda2 {
@Data
@AllArgsConstructor
static class Employee{
private Integer id;
private Integer age; //年龄
private String gender; //性别
private String firstName;
private String lastName;
public static Predicate<Employee> ageGt70 = e -> e.getAge() > 70;
public static Predicate<Employee> genderIsMan = e -> e.getGender().equals("M");
}
public static void main(String[] args) {
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
employees.stream()
.filter(e -> e.getAge()> 60 && e.getGender().equals("M")) // 过滤出来年纪大于60 的男性
.collect(Collectors.toList());
// filter 源码里面放的是Predicate 翻译过来就是谓语 谓语就是修饰主语的 比如说上述例子 员工的修饰语就是大于60 并且是男性
// 还可以在员工实体里面编写谓语: 这种写法就可以复用
// public static Predicate<Employee> ageGt70 = e -> e.getAge() > 70;
// public static Predicate<Employee> genderIsMan = e -> e.getGender().equals("M");
employees.stream()
.filter(
Employee.ageGt70.and // 年纪大于70
(Employee.genderIsMan) // 是男性
.negate() // 取反
).collect(Collectors.toList());
}
}
map对集合数据处理(无状态)
// map处理
// 过年后员工年纪加1 F 变成fman M 变成Man
employees.stream()
.map(e ->{
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")? "male":"female");
return e;
}).collect(Collectors.toList());
// map中几种特殊用法 想上述这种 return回来的和原来是一样 可以使用peek
employees.stream()
.peek(e ->{
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")? "male":"female");
}).collect(Collectors.toList());
// 还以把数据必成其它类型 比如int
List<String> list = Arrays.asList("323", "sasa", "ddffff", "asasas");
list.stream()
.mapToDouble(String::length) // 可以替换 s -> s.length();
.forEach(System.out::println);
// 展开的map flatmap 输出结果 【3,2,3,s,a,s,a】
List<String> collect = list.stream()
.flatMap(x -> Arrays.stream(x.split("")))
.collect(Collectors.toList());
System.out.println(collect);
}
有状态的几种简单api
// 有状态的几种简单api
List<String> nameList = Arrays.asList("tom", "anny", "ming", "tom");
nameList.stream()
.limit(2) // 取前两个数据
.forEach(System.out::println);
nameList.stream()
.skip(2) // 跳过前两个
.forEach(System.out::println);
nameList.stream()
.distinct() // 去重
.forEach(System.out::println);
nameList.stream()
.sorted() // 排序
.forEach(System.out::println);
操作并行
适用于: 数组 arraylist 这种数据已拆分的数据 无状态的操作
不适用: 有操作的状态不适用 最简单的例子 limit(3) 假设分成了三组 就没法保证取到想要的结果
// 无状态下可以实现并行操作 最大程度的利用计算机多核属性 大大增快for循环的速度
nameList.stream()
.parallel() // 并行操作
.filter(x -> x.startsWith("t"))
.collect(Collectors.toList());
排序
// 排序 默认排序是 自然排序 还有一种是大小写不敏感
nameList.sort(Comparator.naturalOrder()); // 自然排序 首字母 排序
nameList.sort(String.CASE_INSENSITIVE_ORDER); // 大小写不敏感 可以理解成全部转换成小写再排序
// 对员工排序 按照firstName正序 年龄正序的排序
employees.stream()
.sorted(
Comparator.comparing(Employee::getFirstName)
.thenComparingInt(Employee::getAge)
);
// 按照员工排序 按照firstName倒叙 年龄正序的排序
employees.stream()
.sorted(
Comparator.comparing(Employee::getFirstName)
.reversed() // 倒叙
.thenComparingInt(Employee::getAge)
);
// 这里要注意 当上面两个都加上 reversed() 第一个会负负得正 即名字正序 年纪倒叙
employees.stream()
.sorted(
Comparator.comparing(Employee::getFirstName)
.reversed()
.thenComparingInt(Employee::getAge)
.reversed()
);
// 都是倒叙 只需要再最后加reversed() 即可
employees.stream()
.sorted(
Comparator.comparing(Employee::getFirstName)
.thenComparingInt(Employee::getAge)
.reversed()
);
函数式接口的特点
1.接口有且仅有一个抽象方法
2.有FunctionInterface注解
3.里面可以实现java object的public方法 比如说equals
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5UU4BnnD-1606057499141)(C:\Users\BAI\AppData\Roaming\Typora\typora-user-images\image-20201122214921666.png)]
4.允许定义静态非抽象方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-biSw0V1X-1606057499143)(C:\Users\BAI\AppData\Roaming\Typora\typora-user-images\image-20201122214853122.png)]
有default修饰的方法体
java8之后才出现的 为了解决新增一个接口方法 就要修改所有实现类
函数式接口就可以直接使用lambda表达式 比如说Runnable Comparator等
// 自定义排序
employees.sort((o1,o2) ->{
if (o1.getAge() == o2.getAge()) {
return 0;
}
return o1.getAge()-o2.getAge()>0 ? 1:-1;
});
查找和匹配
// 是否存在大于70岁的员工
boolean anyMatchAgeGt70 = employees.stream()
.anyMatch(Employee.ageGt70);
System.out.println(anyMatchAgeGt70);
// 所有员工都大于70岁
boolean allMatchAgeGt70 = employees.stream()
.allMatch(Employee.ageGt70);
System.out.println(allMatchAgeGt70);
// 不匹配
employees.stream()
.noneMatch(Employee.ageGt70);
// 查找大于70的员工的第一个 是否有
employees.stream()
.filter(Employee.ageGt70)
.findFirst() // 查找大于70 的第一个元素
.isPresent(); // 看是否存在
// 查找大于70的员工 随便 如果有就输出
employees.stream()
.filter(Employee.ageGt70)
.findAny() // 查找大于70 的第一个元素
.ifPresent(e-> System.out.println(e));
分组
// 分组 按照性别加名字 作为key分组
Map<String, List<Employee>> collect1 = employees.stream()
.collect(Collectors.groupingBy(e -> e.getGender() + "_" + e.getFirstName()));
System.out.println(collect1);
// 分组 按照性别 再按照名字
Map<String, Map<String, List<Employee>>> collect2 = employees.stream()
.collect(Collectors.groupingBy(Employee::getGender, Collectors.groupingBy(Employee::getFirstName)));
System.out.println(collect1);
集合元素规约
// 规约合并 年纪的集合
List<Integer> collect3 = employees.stream()
.map(Employee::getAge).collect(Collectors.toList());
// 串行对所有员工求和
Integer sum = employees.stream()
.map(Employee::getAge)
.reduce(0, Integer::sum); // 0表示初始值 后面表示年纪求和
// 并行对所有员工求和 并行可以大大提高效率
Integer sumParallel = employees.stream()
.parallel()
.map(Employee::getAge)
.reduce(0, Integer::sum, Integer::sum); // 0 初始值
// 第一个分组里面的数据求和 例如分成两组 第一组求和 第二组求和
// 各个组求和 合并第一组和第二组的
/ 0表示初始值 后面表示年纪求和
// 并行对所有员工求和 并行可以大大提高效率
Integer sumParallel = employees.stream()
.parallel()
.map(Employee::getAge)
.reduce(0, Integer::sum, Integer::sum); // 0 初始值
// 第一个分组里面的数据求和 例如分成两组 第一组求和 第二组求和
// 各个组求和 合并第一组和第二组的