大前提:jdk8 允许lambda表达式 最好在maven中加入
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
只总结真香案例!!!
案例一:
从"1" "2" "bilibili" "of" "codesheep" "5" "codesheep" "5" "at" "BILIBILI" "codesheep" "23" "CHEERS" "6"中
找出所有 长度>=5的字符串,并且忽略大小写、去除重复字符串,然后按字母排序,最后用“ * ”连接成一个字符串输出
List<String> list = new ArrayList<>();
list.add("1");
···
list,add("6");
String str = list.stream()//list转为stream流
.filter(i -> !isNum(i))//过滤出不是数字的 也就是 得到字符 isNum是自己封装的方法
.filter(i -> i.length()>5)//过滤出长度大于5的
.map(i -> i.toLowerCase())//转为小写
.distinct()//去重
.sorted(Comparator.naturalOrder())//字符串排序
.collect(Collectors.joining("*"));//字符串连接
System.out.println(str);
案例二:
public class Lambda2 {
public static void main(String[] args) {
/**
* 函数接口:Predict
* 抽象方法:test(T t)
* 参数:T
* 返回值类型:Boolean
* 功能:判断真假
* 示例:判断学生身高
* 结果:韩非的身高高于185吗?: false
*/
Predicate<Integer> predicate = x -> x > 185;
Student student = new Student("韩非", 23, 175);
System.out.println("韩非的身高高于185吗?: " + predicate.test(student.getLength()));
/**
* 函数接口:Consumer
* 抽象方法:accept(T t)
* 参数:T
* 返回值类型:void
* 功能:消费消息
* 示例:输出值
* 结果:列东方位 青龙
*/
Consumer<String> consumer = System.out::println;
consumer.accept("列东方位 青龙");
/**
* 函数接口:Function
* 抽象方法:R apply(T t)
* 参数:T
* 返回值类型:R
* 功能:将T映射为R(转换功能)
* 示例:输出student名字
* 结果:韩非
*
* 备注:Student::getName这种lambda表达式写法被称为 方法引用 格式为 ClassName::MethodName
*/
Function<Student, String> function = Student::getName;
String name = function.apply(student);
System.out.println(name);
/**
* 函数接口:Supplier
* 抽象方法:T get()
* 参数:void
* 返回值类型:T
* 功能:生产消息
* 示例:工厂方法
* 结果:10
*/
Supplier<Integer> supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
System.out.println(supplier.get());
/**
* 函数接口:UnaryOperator
* 抽象方法:T apply(T t)
* 参数:T
* 返回值类型:T
* 功能:一元操作
* 示例:逻辑非(!)
* 结果:false
*/
UnaryOperator<Boolean> unaryOperator = uglily -> !uglily;
Boolean apply2 = unaryOperator.apply(true);
System.out.println(apply2);
//使用BinaryOperator函数式接口 抽象方法T apply(T t, U u) 功能:二元操作 参数T,U 返回值类型T 求两个数乘积
/**
* 函数接口:BinaryOperator
* 抽象方法:T apply(T t, U u)
* 参数:T,U
* 返回值类型:T
* 功能:二元操作
* 示例:求两个数乘积
* 结果: 6
*/
BinaryOperator<Integer> operator = (x, y) -> x * y;
Integer integer = operator.apply(2, 3);
System.out.println(integer);
/**
* 演示函数式接口
* 结果:我是一个演示的函数式接口
*/
test(() -> "我是一个演示的函数式接口");
}
/**
* 演示自定义函数式接口使用
*
* @param worker
*/
public static void test(Worker worker) {
String work = worker.work();
System.out.println(work);
}
public interface Worker {
String work();
}
}
案例三:
/**
* 惰性求值:只描述Stream,操作的结果也是Stream,这样的操作称为惰性求值。惰性求值可以像建造者模式一样链式使用,最后再使用及早求值得到最终结果。
* 及早求值:得到最终的结果而不是Stream,这样的操作称为及早求值。
*
* 常用的流:collect(Collectors.toList())
* 功能:将流转为list
* 结果:[Student(name=路飞, age=22, length=175, specialisies=null), Student(name=红发, age=40, length=180, specialisies=null)]
*/
List<Student> studentList = Stream.of(
new Student("路飞", 22, 175),
new Student("红发", 40, 180)
).collect(Collectors.toList());
System.out.println(studentList);
案例四:
/**
* 常用的流:filter
* 功能:顾名思义,起过滤筛选的作用。内部就是Predicate接口。惰性求值。
* 示例:筛选出身高小于180的同学
* 结果:[Student(name=路飞, age=22, length=175, specialisies=null)]
*/
List<Student> students = new ArrayList<>(3);
students.add(new Student("路飞", 22, 175));
students.add(new Student("红发", 40, 180));
students.add(new Student("白胡子", 50, 185));
List<Student> list = students.stream()
.filter(stu -> stu.getLength() < 180)
.collect(Collectors.toList());
System.out.println(list);
案例五:
/**
* 常用的流:map
* 功能:转换功能,内部就是Function接口。惰性求值
* 示例:获取所有同学的名字
* 结果:[路飞, 红发, 白胡子]
*/
List<Student> students = new ArrayList<>(3);
students.add(new Student("路飞", 22, 175));
students.add(new Student("红发", 40, 180));
students.add(new Student("白胡子", 50, 185));
List<String> names = students.stream().map(student -> student.getName())
.collect(Collectors.toList());
System.out.println(names);
案例六:
/**
* 常用的流:flatMap
* 功能:将多个Stream合并为一个Stream。惰性求值
* 示例:调用Stream.of的静态方法将两个list转换为Stream,再通过flatMap将两个流合并为一个
* 结果:[
* Student(name=路飞, age=22, length=175, specialisies=null),
* Student(name=红发, age=40, length=180, specialisies=null),
* Student(name=白胡子, age=50, length=185, specialisies=null),
* Student(name=艾斯, age=25, length=183, specialisies=null),
* Student(name=雷利, age=48, length=176, specialisies=null)
* ]
*/
List<Student> students = new ArrayList<>(3);
students.add(new Student("路飞", 22, 175));
students.add(new Student("红发", 40, 180));
students.add(new Student("白胡子", 50, 185));
List<Student> studentList = Stream.of(students,
Arrays.asList(new Student("艾斯", 25, 183),
new Student("雷利", 48, 176)))
.flatMap(students1 -> students1.stream()).collect(Collectors.toList());
System.out.println(studentList);
案例七:
/**
* 常用的流:max min
* 功能:我们经常会在集合中求最大或最小值,使用流就很方便。及早求值。
* 示例:得到年龄最大和最小的同学
* 结果:Student(name=白胡子, age=50, length=185, specialisies=null)
* Student(name=路飞, age=22, length=175, specialisies=null)
*
* 补充:max、min接收一个Comparator(例子中使用java8自带的静态函数,只需要传进需要比较值即可。)
* 并且返回一个Optional对象,该对象是java8新增的类,专门为了防止null引发的空指针异常。可以使用max.isPresent()判断是否有值;
* 可以使用max.orElse(new Student()),当值为null时就使用给定值;也可以使用max.orElseGet(() -> new Student());
* 这需要传入一个Supplier的lambda表达式。
*/
List<Student> students = new ArrayList<>(3);
students.add(new Student("路飞", 22, 175));
students.add(new Student("红发", 40, 180));
students.add(new Student("白胡子", 50, 185));
Optional<Student> max = students.stream()
.max(Comparator.comparing(stu -> stu.getAge()));
Optional<Student> min = students.stream()
.min(Comparator.comparing(stu -> stu.getAge()));
//判断是否有值
if (max.isPresent()) {
System.out.println(max.get());
}
if (min.isPresent()) {
System.out.println(min.get());
}
案例八:
/**
* 常用的流:count
* 功能:统计功能,一般都是结合filter使用,因为先筛选出我们需要的再统计即可。及早求值
* 示例:统计年龄小于45的人数
* 结果:年龄小于45岁的人数是:2
*/
public static void main(String[] args) {
List<Student> students = new ArrayList<>(3);
students.add(new Student("路飞", 22, 175));
students.add(new Student("红发", 40, 180));
students.add(new Student("白胡子", 50, 185));
long count = students.stream().filter(s1 -> s1.getAge() < 45).count();
System.out.println("年龄小于45岁的人数是:" + count);
}
案例九:
/**
* 常用的流:reduce
* 功能:reduce 操作可以实现从一组值中生成一个值。前面案例用到的 count 、 min 和 max 方法,因为常用而被纳入标准库中。事实上,这些方法都是 reduce 操作。及早求值。
* 示例:reduce接收了一个初始值为0的累加器,依次取出值与累加器相加,最后累加器的值就是最终的结果
* 结果:10
*/
public static void main(String[] args) {
Integer reduce = Stream.of(1, 2, 3, 4).reduce(0, (acc, x) -> acc+ x);
System.out.println(reduce);
}
案例十:
/**
* 收集器,一种通用的、从流生成复杂值的结构。只要将它传给 collect 方法,所有
* 的流就都可以使用它了。标准类库已经提供了一些有用的收集器,以下示例代码中的收集器都是从 java.util.stream.Collectors 类中静态导入的。
*
*
* 结果:人数最多的班级是:一班 一班平均年龄是:37.666666666666664
*/
public static void main(String[] args) {
List<Student> students1 = new ArrayList<>(3);
students1.add(new Student("路飞", 23, 175));
students1.add(new Student("红发", 40, 180));
students1.add(new Student("白胡子", 50, 185));
OutstandingClass ostClass1 = new OutstandingClass("一班", students1);
//复制students1,并移除一个学生
List<Student> students2 = new ArrayList<>(students1);
students2.remove(1);
OutstandingClass ostClass2 = new OutstandingClass("二班", students2);
//将ostClass1、ostClass2转换为Stream
Stream<OutstandingClass> classStream = Stream.of(ostClass1, ostClass2);
OutstandingClass outstandingClass = biggestGroup(classStream);
System.out.println("人数最多的班级是:" + outstandingClass.getName());
System.out.println("一班平均年龄是:" + averageNumberOfStudent(students1));
}
/**
* 获取人数最多的班级
* 无论选择哪种返回值语句都可以
* 区别是 Optional中对null做了许多封装,可以进行空值的校验
* orElseGet方法 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
*/
private static OutstandingClass biggestGroup(Stream<OutstandingClass> outstandingClasses) {
// return outstandingClasses.max(Comparator.comparing(stu -> stu.getStudents().size())).get();
return Optional.of(outstandingClasses.max(Comparator.comparing(stu -> stu.getStudents().size())).get()).orElseGet(OutstandingClass:: new );
}
/**
* 计算平均年龄
*/
private static double averageNumberOfStudent(List<Student> students) {
return students.stream().collect(Collectors.averagingInt(Student::getAge));
}
案例十一:
/**
* 常用的流:Predicate
* 功能:将流分解成两个集合,接收一个Predicate函数式接口。
* 示例:按照会sing的和不会sing对学生进行分拆
* 结果:{
* false=[Student(name=红发, age=40, length=180, specialisies=swimming), Student(name=白胡子, age=50, length=185, specialisies=dance)],
* true=[Student(name=路飞, age=23, length=175, specialisies=sing)]
* }
* 和Collectors.groupingBy() 分组类似
*/
List<Student> students = new ArrayList<>(3);
Student stu1 = new Student("路飞", 23, 175);
stu1.setSpecialisies("sing");
students.add(stu1);
Student stu2 = new Student("红发", 40, 180);
stu2.setSpecialisies("swimming");
students.add(stu2);
Student stu3 = new Student("白胡子", 50, 185);
stu3.setSpecialisies("dance");
students.add(stu3);
Map<Boolean, List<Student>> listMap = students.stream().collect(
Collectors.partitioningBy(stu ->
stu.getSpecialisies().
contains(SpecialityEnum.SING.name)));
System.out.println(listMap);
//使用Collectors.groupingBy()也可完成同样的功能
Map<Boolean, List<Student>> li = students.stream().collect(Collectors.groupingBy(student -> student.getSpecialisies().contains(SpecialityEnum.SING.name)));
System.out.println(li);
案例十二:
/**
* 常用的流:数据分组groupingBy 分组条件通常会被作为Map的key
* 功能:数据分组是一种更自然的分割数据操作,与将数据分成 ture 和 false 两部分不同,可以使用任意值对数据分组。Collectors.groupingBy接收一个Function做转换。
* 示例:按照会特长对学生进行分组
* 结果:{
* swimming=[Student(name=红发, age=40, length=180, specialisies=swimming)],
* sing=[Student(name=路飞, age=23, length=175, specialisies=sing)],
* dance=[Student(name=白胡子, age=50, length=185, specialisies=dance)]
* }
* 分组条件可以拼接多个 但是要和声明的类型(Map的key)一致
* 示例:声明类型是String
* 分组条件:特长***名字 stu.getSpecialisies()+"***"+stu.getName()
* 结果:{
* dance***白胡子=[Student(name=白胡子, age=50, length=185, specialisies=dance)],
* sing***路飞=[Student(name=路飞, age=23, length=175, specialisies=sing)],
* swimming***红发=[Student(name=红发, age=40, length=180, specialisies=swimming)]
* }
*/
List<Student> students = new ArrayList<>(3);
Student stu1 = new Student("路飞", 23, 175);
stu1.setSpecialisies("sing");
students.add(stu1);
Student stu2 = new Student("红发", 40, 180);
stu2.setSpecialisies("swimming");
students.add(stu2);
Student stu3 = new Student("白胡子", 50, 185);
stu3.setSpecialisies("dance");
students.add(stu3);
Map<String, List<Student>> listMap =
students.stream().collect(
Collectors.groupingBy(stu -> stu.getSpecialisies()));
System.out.println(listMap);
Map<String, List<Student>> lis =
students.stream().collect(
Collectors.groupingBy(stu -> stu.getSpecialisies()+"***"+stu.getName()));
System.out.println(lis);
案例十三:
/**
* 常用的流:拼接joining 有三个参数,第一个是分隔符,第二个是前缀符,第三个是后缀符。也可以不传入参数Collectors.joining(),这样就是直接拼接。
* 功能:类似stringbuilder这样循环append("a") append("b") append("c")拼接
* 示例:将所有学生的名字拼接起来
* 结果:[路飞,红发,白胡子]
*/
List<Student> students = new ArrayList<>(3);
students.add(new Student("路飞", 22, 175));
students.add(new Student("红发", 40, 180));
students.add(new Student("白胡子", 50, 185));
String names = students.stream()
.map(Student::getName).collect(Collectors.joining(",","[","]"));
System.out.println(names);
案例十四:
/**
* 接口:Consumer 顾名思义,它是“消费者的含义”,接受参数而不返回值
* 示例:打印消息
* 结果:hello world
* hello java
* hhhhh
* hhhhh
* hhhhh
*/
Consumer c = System.out::println;
c.accept("hello world");//打印hello world
c.accept("hello java");//打印hello java
c.andThen(c).andThen(c).accept("hhhhh");//连续打印三次hhhhh 而不是hello java hello java hhhhh
案例十五:
/**
* 接口:Function 代表的含义是“函数”,其实和上面的 Consumer有点像,不过 Function既有输入,也有输出,使用更加灵活
* 示例:对一个整数先乘以 2,再计算平方值
* 结果:36 5184
* 过程:先走apply传递参数3 然后执行f1=3*2得到6 然后执行f2=6*6得到36
* 先走apply传递参数3 然后执行f1=3*2得到6 然后执行f2=6*6得到36 然后执行f1=36*2得到72 然后执行f2=72*72得到5184
* andThen的参数就是 Function类型的after 即接下里执行的函数
*/
Function<Integer,Integer> f1 = i -> i*2;
Function<Integer,Integer> f2 = i -> i*i;
Consumer c = System.out::println;
c.accept(f1.andThen(f2).apply(3));
c.accept(f1.andThen(f2).andThen(f1).andThen(f2).apply(3));
案例十六:空值校验
/**
* 接口:Optional 本质是个容器,你可以将你的变量交由它进行封装,这样我们就不用显式对原变量进行 null值检测,防止出现各种空指针异常
* 示例:获取学生科目的成绩
*/
public static Integer getScore(Student stu){
return Optional.ofNullable(stu).map(Student::getSubject).map(Subject::getScore).orElse(null);
}
//常规做法 判断学生是否为空,不为空得到学科,判断学科是否为空,不为空取值 如果有很多层包着 简直是俄罗斯套娃 惨不忍睹
public static Integer getScore(Student stu){
if(stu != null){
Subject sub = stu.getSubject();
if (sub != null){
return sub.getScore();
}
}
return null;
}