栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA流式编程和Lambda表达式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA流式编程和Lambda表达式

参考链接: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 Lambda

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());

Comparator comparator2 = Integer::compare;//类名::静态方法名
System.out.println(comparator2.compare(40, 30));
4 流式编程 4.1 集合的流式编程的简介
	Stream是JDK1.8之后出现的新特性,也是JDK1.8新特性中最值得学习的两种新特性之一(另一个是lambda表达式)Stream是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储的。流更像是一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环。
流式编程是对集合操作的简化。

为什么要使用集合的流式编程

	有些时候,对集合中的元素进行操作的时候,需要使用到其他操作的结果。在这个过程中,集合的流式编程可以大幅度的简化代码的数量。将数据源中的数据,读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射...)。每次的操作结果也是一个流对象,可以对这个流再进行其它的操作。

带来代码难理解的问题?可能是对流式i编程的不理解,可添加注释表明流式编程语句段用于实现什么功能。
流式编程的出现可配合函数式编程+builder设计模式,每次的调用都返回集合对象,方法中传入的参数是Java预先定义的函数式接口(上面附录中出现的接口)可使用lambda表达式进行快速实现,简化大量代码。

流操作包括:

创建流
修改流元素(中间操作,Intermidate Operations)
消费流元素(终端操作,Terminal Operations)
4.2 流创建
  1. Stream.of
Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!")
                .forEach(System.out::print);
  1. .stream
Set w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" ")));
w.stream()
.map(x -> x + " ")
.forEach(System.out::print);
4.3 中间方法

将数据从数据源中读取到流中,就是对流中的数据进行各种各样的操作、处理。中间操作可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作,直到最终操作。

中间操作主要分为:filter、distinct、sorted、limit&skip、map&flatMap、Collections工具类
  1. filter
    条件过滤,仅保留流中满足指定条件的数据,其他不满足的数据都会被删除。
 //2. 过滤掉集合中成绩不合格的学生信息
studentList.stream().filter(s -> s.getScore()>=60).forEach(System.out::println);
  1. distinct
    去除集合中重复的元素,这个方法没有参数,去重的规则与HashSet相同。
    (1)比较两个对象的hashcode
    (2)使用equals再来对比一下
    这里要注意:实体类中需要重写hashcode和equals方法,否则去重可能不会生效
studentList.stream().distinct().forEach(System.out::println);
  1. sorted
    对流中的数据进行排序
    (1)无参:sorted():将流中的数据,按照其对应的类实现的Comparable接口提供的比较规则进行排序
    (2)有参:sorted(Comparator comparator):将流中的数据,按照参数接口提供的比较规则进行排序
 //3. 对流中的数据按照自定义的规则进行排序 (按照年龄升序排列)
studentList.stream().sorted((s1,s2)->s1.getAge()-s2.getAge()).forEach(System.out::println);
  1. 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);
  1. map&flatMap
 Stream map(Function mapper);

对流中的数据进行映射,用新的数据替换旧的数据
map最主要,就是来做元素的替换,其实map是一个元素的映射(将流中每一个元素替换成指定的元素)

//2. 实际需求:获取所有学生的名字
studentList.stream().map(Student::getName).forEach(System.out::println);
studentList.stream().map(s->s.getName()).forEach(System.out::println);
//3. 实际需求:获取所有学生的成绩 注意:泛型中不能是基本数据类型,只能是包装类 即下面的返回值应该为Stream
studentList.stream().map(Student::getScore).forEach(System.out::println);
//4. 实际需求:获取所有学生的成绩
IntSummaryStatistics intSummaryStatistics = studentList.stream().mapToInt(Student::getScore).summaryStatistics();

flatMap也是元素的映射,flatMap是扁平化映射
扁平化映射:一般用在map映射完成后,流中的数据是一个容器,而我们需要对容器中的数据进行处理 此时使用扁平化映射,可以将流中的容器中的数据,直接读取到流中

  1. 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.流中的数据的聚合
List list = 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
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/397000.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号