参考链接:onjava8 函数式编程与流式编程:https://blog.csdn.net/wangzibai/article/details/108900712
Lambda表达式和流式编程:https://www.cnblogs.com/zhangyaru/p/15160621.html
【java】集合流式编程总结:https://www.cnblogs.com/flyinghome/p/13656637.html
1.1 无参无返回值
() -> {System.out.println("lambda无参无返回值,一条语句");};
() -> System.out.println("lambda无参无返回值,一条语句时{}可省略");
lambda表达式的语句体只有一条时,可以省略{},多条时不可省略
lambda表达式只能引用被final修饰的外层局部变量
1.2 有一个参数无返回值
(n) -> { System.out.println(n + 100); };
n -> System.out.println(n + 100);
lambda表达式只有一个参数时,参数类型可以省略,参数名可以是任意名称 lambda表达式只有一个参数时,参数外的()可以省略
1.3 多个参数无返回值
(n, str) -> {
System.out.println(n + "===" + str);
System.out.println("多个参数");
};
lambda表达式有多个参数时,参数外的()不可省略
1.4 有参数有返回值
(n) -> { return n;};
(n) -> n;
有返回值的lambda表达式,如果方法体只有一条语句,可以同时省略{}和return
虽然lambda表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以用lambda表达式来实现。
lambda规定接口中只能有一个需要被实现的抽象方法,不是规定接口中只能有一个方法,称为函数式接口
1.5 总结
->左边:
参数类型可以不写 如果只有一个参数,()可以省略
->右边:
{}将方法体的具体内容包裹起来
方法体只有一条语句时,{}可以省略不写
方法体只有一条语句,且有返回值时,{}和return可以省略不写
2 函数式接口
接口类上加@FunctionalInterface注解的是函数式接口 函数式接口只能有一个抽象方法,其他的可以有default,static,Object里的public方法等 作用:在java中主要用在lambda表达式上 Comparator,Runnable,Callable都是函数式接口(注意:Comparable并没有被标记为函数式接口)
2.1 JAVA常用的内置函数式接口
2.1.1 消费型接口
代码示例1(遍历list):
public class FunInterface {
public static void main(String[] args) {
List list = new ArrayList();
Collections.addAll(list, 10, 40, 20, 40, 30, 54, 36);
System.out.println(list);
//匿名内部类遍历
Consumer consumer = new Consumer() {
@Override
public void accept(Integer o) {
System.out.println(o);//o就是list中的元素
}
};
list.forEach(consumer);
//lambda表达式遍历
list.forEach((elem) -> {
System.out.println(elem);
});
}
}
代码示例2(遍历map):
public class FunInterface1 {
public static void main(String[] args) {
Map map = new HashMap();
map.put("cn", "China");
map.put("jp", "Japan");
map.put("us", "the United States");
map.put("us", "America");
map.put("uk", "England");
map.put("en", "England");
//匿名内部类遍历
BiConsumer biConsumer = new BiConsumer() {
@Override
public void accept(String o, String o2) {
System.out.println(o+"------>"+o2);
}
};
map.forEach(biConsumer);
//lambda表达式遍历
map.forEach((o,o2)-> System.out.println(o+"--->"+o2));
}
}
2.1.2 断言型接口
public class FunInterface3 {
public static void main(String[] args) {
List list = new ArrayList();
Collections.addAll(list, 10, 40, 20, 40, 30, 60, 80, 70);
//匿名内部类
Predicate predicate = new Predicate() {
@Override
public boolean test(Integer o) {
if (o < 60) {
return true;
}
return false;
}
};
list.removeIf(predicate);
System.out.println(list);
List list2 = new ArrayList();
Collections.addAll(list2, 10, 40, 20, 40, 30, 60, 80, 70);
//lambda表达式
list2.removeIf(elem -> {
if (elem < 60) return true;
return false;
});
System.out.println(list2);
}
}
2.1.3 供给型
public class MethDemo02 {
public static void main(String[] args) {
Student student = new Student(1, "zhangsan", 89.0, 23);
//匿名内部类
Supplier supplier = new Supplier() {
@Override
public String get() {
return student.getName();
}
};
System.out.println(supplier.get());
//lambda表达式
Supplier supplier1 = () -> student.getName();
System.out.println(supplier1.get());
}
}
这些接口会在下面的流式编程中大量使用,用于传递,编程者自定义的实现
3.方法引用当lambda表达式的方法体仅仅调用一个已经存在的方法,而不做任何其他的操作,对于这种情况,可以通过一个方法名来引用已经存在的方法,属于简化版的lambda表达式
方法引用的操作符是双冒号 ::
//简化为方法引用: list.forEach(System.out::println);//对象引用::实例方法名 Supplier supplier2 = student::getName;//对象引用::实例方法名 System.out.println(supplier2.get()); Comparator4 流式编程 4.1 集合的流式编程的简介comparator2 = Integer::compare;//类名::静态方法名 System.out.println(comparator2.compare(40, 30));
Stream是JDK1.8之后出现的新特性,也是JDK1.8新特性中最值得学习的两种新特性之一(另一个是lambda表达式)Stream是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储的。流更像是一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环。 流式编程是对集合操作的简化。
为什么要使用集合的流式编程
有些时候,对集合中的元素进行操作的时候,需要使用到其他操作的结果。在这个过程中,集合的流式编程可以大幅度的简化代码的数量。将数据源中的数据,读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射...)。每次的操作结果也是一个流对象,可以对这个流再进行其它的操作。
带来代码难理解的问题?可能是对流式i编程的不理解,可添加注释表明流式编程语句段用于实现什么功能。
流式编程的出现可配合函数式编程+builder设计模式,每次的调用都返回集合对象,方法中传入的参数是Java预先定义的函数式接口(上面附录中出现的接口)可使用lambda表达式进行快速实现,简化大量代码。
流操作包括:
创建流 修改流元素(中间操作,Intermidate Operations) 消费流元素(终端操作,Terminal Operations)4.2 流创建
- Stream.of
Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!")
.forEach(System.out::print);
- .stream
Set4.3 中间方法w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" "))); w.stream() .map(x -> x + " ") .forEach(System.out::print);
将数据从数据源中读取到流中,就是对流中的数据进行各种各样的操作、处理。中间操作可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作,直到最终操作。
中间操作主要分为:filter、distinct、sorted、limit&skip、map&flatMap、Collections工具类
- filter
条件过滤,仅保留流中满足指定条件的数据,其他不满足的数据都会被删除。
//2. 过滤掉集合中成绩不合格的学生信息 studentList.stream().filter(s -> s.getScore()>=60).forEach(System.out::println);
- distinct
去除集合中重复的元素,这个方法没有参数,去重的规则与HashSet相同。
(1)比较两个对象的hashcode
(2)使用equals再来对比一下
这里要注意:实体类中需要重写hashcode和equals方法,否则去重可能不会生效
studentList.stream().distinct().forEach(System.out::println);
- sorted
对流中的数据进行排序
(1)无参:sorted():将流中的数据,按照其对应的类实现的Comparable接口提供的比较规则进行排序
(2)有参:sorted(Comparator comparator):将流中的数据,按照参数接口提供的比较规则进行排序
//3. 对流中的数据按照自定义的规则进行排序 (按照年龄升序排列) studentList.stream().sorted((s1,s2)->s1.getAge()-s2.getAge()).forEach(System.out::println);
- limit&skip
limit:限制,截取流中指定数量的元素 skip:跳过流中的指定数量的元素 经常放在一起用
//3. 获取成绩为3-5名的
studentList.stream().distinct()
.sorted((s1,s2)->s2.getScore()-s1.getScore())
.limit(5)
.skip(2)
.forEach(System.out::println);
- map&flatMap
Stream map(Function super T, ? extends R> mapper);
对流中的数据进行映射,用新的数据替换旧的数据
map最主要,就是来做元素的替换,其实map是一个元素的映射(将流中每一个元素替换成指定的元素)
//2. 实际需求:获取所有学生的名字 studentList.stream().map(Student::getName).forEach(System.out::println); studentList.stream().map(s->s.getName()).forEach(System.out::println); //3. 实际需求:获取所有学生的成绩 注意:泛型中不能是基本数据类型,只能是包装类 即下面的返回值应该为StreamstudentList.stream().map(Student::getScore).forEach(System.out::println); //4. 实际需求:获取所有学生的成绩 IntSummaryStatistics intSummaryStatistics = studentList.stream().mapToInt(Student::getScore).summaryStatistics();
flatMap也是元素的映射,flatMap是扁平化映射
扁平化映射:一般用在map映射完成后,流中的数据是一个容器,而我们需要对容器中的数据进行处理 此时使用扁平化映射,可以将流中的容器中的数据,直接读取到流中
- Collections工具类
Collectors是一个工具类,里面封装了很多方法,可以很方便的获取到一个Collector接口的实现类对象,从而可以使用collect()方法,对流中的数据,进行各种各样的处理、整合。
常用方法:
Collectors.toList() 将流中的数据,聚合到一个List集合中 Collectors.toSet() 将流中的数据,聚合到一个Set集合中 Collectors.toMap() 将流中的数据,聚合到一个Map集合中 maxBy() 按照指定的规则,找到流中最大的元素,等同于max minBy() 按照指定的规则,找到流中最小的元素,等同于min joining() 将流中的数据拼接成一个字符串,注意:只能操作流中是String的数据 summingInt() 将流中的数据,映射成int类型的数据,并求和 averagingInt() 将流中的数据,映射成int类型的数据,并求平均值 summarizingInt() 将流中的数据,映射成int类型的数据,并获取描述信息4.4 最终操作
数组
toArray():将流转换成适当类型的数组。 toArray(generator):在特殊情况下,生成自定义类型的数组。
循环
forEach(Consumer)常见如 System.out::println 作为 Consumer 函数。 forEachOrdered(Consumer): 保证 forEach 按照原始流顺序操作。
集合collect
将流中的数据收集到一起,对这些数据进行一些处理。最常见的处理,就是将流中的数据存入一个集合。collect方法的参数,是一个collector接口,而且这个接口并不是一个函数式接口,实现这个接口,可以自定义收集的规则。但是,绝大部分情况下,不需要自定义。
直接使用Collectors工具类提供的方法即可。
collect(Collector):使用 Collector 收集流元素到结果集合中。 collect(Supplier, BiConsumer, BiConsumer):同上,第一个参数 Supplier 创建了一个新的结果集合,第二个参数 BiConsumer 将下一个元素收集到结果集合中,第三个参数 BiConsumer 用于将两个结果集合合并起来。
//2.流中的数据的聚合 Listlist = dataSource.collect(Collectors.toList()); System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Set set = dataSource.collect(Collectors.toSet()); System.out.println(set);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Map map = dataSource.collect(Collectors.toMap(i -> i.toString(), i -> i));//i.toString作为键 i作为值 Map map = dataSource.collect(Collectors.toMap(Object::toString, i -> i));//i.toString作为键 i作为值
组合reduce
将流中的数据按照一定的规则聚合起来。将流的元素,逐一带入这个方法中,进行运算。最终的运算结果,得到的其实是一个Optional类型,需要使用get()获取到里面的数据
reduce(BinaryOperator):使用 BinaryOperator 来组合所有流中的元素。因为流可能为空,其返回值为 Optional。 reduce(identity, BinaryOperator):功能同上,但是使用 identity 作为其组合的初始值。因此如果流为空,identity 就是结果。 reduce(identity, BiFunction, BinaryOperator):更复杂的使用形式(暂不介绍),这里把它包含在内,因为它可以提高效率。通常,我们可以显式地组合 map() 和 reduce() 来更简单的表达它。
//2.最终操作 (这里有两个参数,实现了从0到9的和的求值 即p1=0 p2=1 和的结果为1作为p1 再跟p2=2相加,以此类推) Integer num = dataSource.reduce((p1, p2) -> p1 + p2).get(); //Integer num = dataSource.reduce(Integer::sum).get(); //3.输出 System.out.println(num);//45
匹配
allMatch(Predicate) :如果流的每个元素提供给 Predicate 都返回 true ,结果返回为 true。在第一个 false 时,则停止执行计算。 anyMatch(Predicate):如果流的任意一个元素提供给 Predicate 返回 true ,结果返回为 true。在第一个 true 是停止执行计算。 noneMatch(Predicate):如果流的每个元素提供给 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算。
//2.匹配的操作
//boolean b = dataSource.allMatch(e -> e>0);
//System.out.println(b);//false 因为不是集合中所有的元素都大于0 还有一个等于0
//boolean b = dataSource.anyMatch(e -> e >= 9);
//System.out.println(b);//true
boolean b = dataSource.noneMatch(e -> e > 9);
System.out.println(b);//true
查找
findFirst():返回第一个流元素的 Optional,如果流为空返回 Optional.empty。 findAny(:返回含有任意流元素的 Optional,如果流为空返回 Optional.empty。
//2.findFirst演示
//Integer integer = dataSource.stream().findFirst().get();//串行流
//Integer integer = dataSource.parallelStream().findFirst().get();//并行流
//System.out.println(integer);//串行流或者是并行流结果都是0
//3.findAny演示
//Integer integer = dataSource.stream().findAny().get();//串行流
Integer integer = dataSource.parallelStream().findAny().get();//并行流
System.out.println(integer);//串行流是0、并行流结果为6 即不一定是0
信息
count():流中的元素个数。 max(Comparator):根据所传入的 Comparator 所决定的“最大”元素。 min(Comparator):根据所传入的 Comparator 所决定的“最小”元素。
//2.最终操作 获取流中的最大、最小值 Integer max = dataSource.max(Integer::compareTo).get(); System.out.println(max);//9 Integer min = dataSource.min(Integer::compareTo).get(); System.out.println(min);//0
数字流信息IntStream
主要可以实现获取流中int类型数据的最大值、最小值、平均值、和、数量
还可以获取到一个对流中数据的分析结果(即一次获取所有类型的值)
average() :求取流元素平均值。 max() 和 min():数值流操作无需 Comparator。 sum():对所有流元素进行求和。 summaryStatistics():生成可能有用的数据。目前并不太清楚这个方法存在的必要性,因为我们其实可以用更直接的方法获得需要的数据。
//3.输出
//System.out.println(stream.max().getAsInt());//获取最大值 9
//System.out.println(stream.min().getAsInt());//获取最小值 0
//System.out.println(stream.sum());//获取和 45
//System.out.println(stream.count());//获取流中数据个数 10
//System.out.println(stream.average().getAsDouble());//获取流中数据的平均值 4.5
//4.获取到一个对流中数据的分析结果(即一次获取所有类型的值)
IntSummaryStatistics intSummaryStatistics = stream.summaryStatistics();
System.out.println("最大值:"+intSummaryStatistics.getMax()+" 最小值:"+intSummaryStatistics.getMin());//最大值:9 最小值:0
System.out.println("平均值:"+intSummaryStatistics.getAverage()+" 和:"+intSummaryStatistics.getSum());//平均值:4.5 和:45
System.out.println("数量:"+intSummaryStatistics.getCount());//数量:10



