感受lambda之美,推荐收藏,需要时查阅

小郝不负流年
小郝不负流年   + 关注
2021-03-29 23:28:44   阅读7   评论0

作者:9龙

来源:juejin.cn/post/6844903849753329678

一、引言 

java8最大的特性就是引入Lambda表达式,即函数式编程,可以将行为进行传递。总结就是:使用不可变值与函数,函数对不可变值进行处理,映射成另一个值。

二、java重要的函数式接口 

1、什么是函数式接口

函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型。使用@FunctionalInterface注解修饰的类,编译器会检测该类是否只有一个抽象方法或接口,否则,会报错。可以有多个默认方法,静态方法。

1.1 java8自带的常用函数式接口。
函数接口抽象方法功能参数返回类型示例
Predicatetest(T t)判断真假Tboolean9龙的身高大于185cm吗?
Consumeraccept(T t)消费消息Tvoid输出一个值
FunctionR apply(T t)将T映射为R(转换功能)TR获得student对象的名字
SupplierT get()生产消息NoneT工厂方法
UnaryOperatorT apply(T t)一元操作TT逻辑非(!)
BinaryOperatorapply(T t, U u)二元操作(T,T)(T)求两个数的乘积(*)
public class Test {
    public static void main(String[] args) {
        Predicate predicate = x -> x > 185;
        Student student = new Student("9龙"23175);
        System.out.println(
            "9龙的身高高于185吗?:" + predicate.test(student.getStature()));

        Consumer consumer = System.out::println;
        consumer.accept("命运由我不由天");

        Function function = Student::getName;
        String name = function.apply(student);
        System.out.println(name);

        Supplier supplier = 
            () -> Integer.valueOf(BigDecimal.TEN.toString());
        System.out.println(supplier.get());

        UnaryOperator unaryOperator = uglily -> !uglily;
        Boolean apply2 = unaryOperator.apply(true);
        System.out.println(apply2);

        BinaryOperator operator = (x, y) -> x * y;
        Integer integer = operator.apply(23);
        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();
    }
}
//9龙的身高高于185吗?:false
//命运由我不由天
//9龙
//10
//false
//6
//我是一个演示的函数式接口
复制代码

以上演示了lambda接口的使用及自定义一个函数式接口并使用。下面,我们看看java8将函数式接口封装到流中如何高效的帮助我们处理集合。

注意:Student::getName例子中这种编写lambda表达式的方式称为方法引用。格式为ClassNmae::methodName。是不是很神奇,java8就是这么迷人。

示例:本篇所有示例都基于以下三个类。OutstandingClass:班级;Student:学生;SpecialityEnum:特长。


1.2 惰性求值与及早求值

惰性求值:只描述Stream,操作的结果也是Stream,这样的操作称为惰性求值。惰性求值可以像建造者模式一样链式使用,最后再使用及早求值得到最终结果。

及早求值:得到最终的结果而不是Stream,这样的操作称为及早求值。

2、常用的流

2.1 collect(Collectors.toList())

将流转换为list。还有toSet(),toMap()等。及早求值

public class TestCase {
    public static void main(String[] args) {
        List studentList = Stream.of(new Student("路飞"22175),
                new Student("红发"40180),
                new Student("白胡子"50185)).collect(Collectors.toList());
        System.out.println(studentList);
    }
}
//输出结果
//[Student{name='路飞', age=22, stature=175, specialities=null}, 
//Student{name='红发', age=40, stature=180, specialities=null}, 
//Student{name='白胡子', age=50, stature=185, specialities=null}]
复制代码
2.2 filter

顾名思义,起过滤筛选的作用。内部就是Predicate接口。惰性求值。


比如我们筛选出出身高小于180的同学。

public class TestCase {
    public static void main(String[] args) {
        List students = new ArrayList<>(3);
        students.add(new Student("路飞"22175));
        students.add(new Student("红发"40180));
        students.add(new Student("白胡子"50185));

        List list = students.stream()
            .filter(stu -> stu.getStature() < 180)
            .collect(Collectors.toList());
        System.out.println(list);
    }
}
//输出结果
//[Student{name='路飞', age=22, stature=175, specialities=null}]
复制代码
2.3 map

转换功能,内部就是Function接口。惰性求值


public class TestCase {
    public static void main(String[] args) {
        List students = new ArrayList<>(3);
        students.add(new Student("路飞"22175));
        students.add(new Student("红发"40180));
        students.add(new Student("白胡子"50185));

        List names = students.stream().map(student -> student.getName())
                .collect(Collectors.toList());
        System.out.println(names);
    }
}
//输出结果
//[路飞, 红发, 白胡子]
复制代码

例子中将student对象转换为String对象,获取student的名字。

2.4 flatMap

将多个Stream合并为一个Stream。惰性求值


public class TestCase {
    public static void main(String[] args) {
        List students = new ArrayList<>(3);
        students.add(new Student("路飞"22175));
        students.add(new Student("红发"40180));
        students.add(new Student("白胡子"50185));

        List studentList = Stream.of(students,
                asList(new Student("艾斯"25183),
                        new Student("雷利"48176)))
                .flatMap(students1 -> students1.stream()).collect(Collectors.toList());
        System.out.println(studentList);
    }
}
//输出结果
//[Student{name='路飞', age=22, stature=175, specialities=null}, 
//Student{name='红发', age=40, stature=180, specialities=null}, 
//Student{name='白胡子', age=50, stature=185, specialities=null}, 
//Student{name='艾斯', age=25, stature=183, specialities=null},
//Student{name='雷利', age=48, stature=176, specialities=null}]
复制代码

调用Stream.of的静态方法将两个list转换为Stream,再通过flatMap将两个流合并为一个。

2.5 max和min

我们经常会在集合中求最大或最小值,使用流就很方便。及早求值。

public class TestCase {
    public static void main(String[] args) {
        List students = new ArrayList<>(3);
        students.add(new Student("路飞"22175));
        students.add(new Student("红发"40180));
        students.add(new Student("白胡子"50185));

        Optional max = students.stream()
            .max(Comparator.comparing(stu -> stu.getAge()));
        Optional 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());
        }
    }
}
//输出结果
//Student{name='白胡子', age=50, stature=185, specialities=null}
//Student{name='路飞', age=22, stature=175, specialities=null}
复制代码

max、min接收一个Comparator(例子中使用java8自带的静态函数,只需要传进需要比较值即可。)并且返回一个Optional对象,该对象是java8新增的类,专门为了防止null引发的空指针异常。可以使用max.isPresent()判断是否有值;可以使用max.orElse(new Student()),当值为null时就使用给定值;也可以使用max.orElseGet(() -> new Student());这需要传入一个Supplier的lambda表达式。

2.6 count

统计功能,一般都是结合filter使用,因为先筛选出我们需要的再统计即可。及早求值

public class TestCase {
    public static void main(String[] args) {
        List students = new ArrayList<>(3);
        students.add(new Student("路飞"22175));
        students.add(new Student("红发"40180));
        students.add(new Student("白胡子"50185));

        long count = students.stream().filter(s1 -> s1.getAge() < 45).count();
        System.out.println("年龄小于45岁的人数是:" + count);
    }
}
//输出结果
//年龄小于45岁的人数是:2
复制代码
2.7 reduce

reduce 操作可以实现从一组值中生成一个值。在上述例子中用到的 count 、 min 和 max 方
法,因为常用而被纳入标准库中。事实上,这些方法都是 reduce 操作。及早求值。


public class TestCase {
    public static void main(String[] args) {
        Integer reduce = Stream.of(1234).reduce(0, (acc, x) -> acc+ x);
        System.out.println(reduce);
    }
}
//输出结果
//10
复制代码

我们看得reduce接收了一个初始值为0的累加器,依次取出值与累加器相加,最后累加器的值就是最终的结果。

三、高级集合类及收集器 

3.1 转换成值

收集器,一种通用的、从流生成复杂值的结构。只要将它传给 collect 方法,所有
的流就都可以使用它了。标准类库已经提供了一些有用的收集器,以下示例代码中的收集器都是从 java.util.stream.Collectors 类中静态导入的。

public class CollectorsTest {
    public static void main(String[] args) {
        List students1 = new ArrayList<>(3);
        students1.add(new Student("路飞"23175));
        students1.add(new Student("红发"40180));
        students1.add(new Student("白胡子"50185));

        OutstandingClass ostClass1 = new OutstandingClass("一班", students1);
        //复制students1,并移除一个学生
        List students2 = new ArrayList<>(students1);
        students2.remove(1);
        OutstandingClass ostClass2 = new OutstandingClass("二班", students2);
        //将ostClass1、ostClass2转换为Stream
        Stream classStream = Stream.of(ostClass1, ostClass2);
        OutstandingClass outstandingClass = biggestGroup(classStream);
        System.out.println("人数最多的班级是:" + outstandingClass.getName());

        System.out.println("一班平均年龄是:" + averageNumberOfStudent(students1));
    }

    /**
     * 获取人数最多的班级
     */
    private static OutstandingClass biggestGroup(Stream outstandingClasses) {
        return outstandingClasses.collect(
                maxBy(comparing(ostClass -> ostClass.getStudents().size())))
                .orElseGet(OutstandingClass::new);
    }

    /**
     * 计算平均年龄
     */
    private static double averageNumberOfStudent(List students) {
        return students.stream().collect(averagingInt(Student::getAge));
    }
}
//输出结果
//人数最多的班级是:一班
//一班平均年龄是:37.666666666666664
复制代码

maxBy或者minBy就是求最大值与最小值。

3.2 转换成块

常用的流操作是将其分解成两个集合,Collectors.partitioningBy帮我们实现了,接收一个Predicate函数式接口。


将示例学生分为会唱歌与不会唱歌的两个集合。

public class PartitioningByTest {
    public static void main(String[] args) {
        //省略List students的初始化
        Map> listMap = students.stream().collect(
            Collectors.partitioningBy(student -> student.getSpecialities().
                                      contains(SpecialityEnum.SING)));
    }
}
复制代码

3.3 数据分组

数据分组是一种更自然的分割数据操作,与将数据分成 ture 和 false 两部分不同,可以使
用任意值对数据分组。Collectors.groupingBy接收一个Function做转换。


如图,我们使用groupingBy将根据进行分组为圆形一组,三角形一组,正方形一组。

例子:根据学生第一个特长进行分组

public class GroupingByTest {
    public static void main(String[] args) {
        //省略List students的初始化
         Map> listMap = 
             students.stream().collect(
             Collectors.groupingBy(student -> student.getSpecialities().get(0)));
    }
}
复制代码

Collectors.groupingBy与SQL 中的 group by 操作是一样的。

3.4 字符串拼接

如果将所有学生的名字拼接起来,怎么做呢?通常只能创建一个StringBuilder,循环拼接。使用Stream,使用Collectors.joining()简单容易。

public class JoiningTest {
    public static void main(String[] args) {
        List students = new ArrayList<>(3);
        students.add(new Student("路飞"22175));
        students.add(new Student("红发"40180));
        students.add(new Student("白胡子"50185));

         String names = students.stream()
             .map(Student::getName).collect(Collectors.joining(",","[","]"));
        System.out.println(names);
    }
}
//输出结果
//[路飞,红发,白胡子]
复制代码

joining接收三个参数,第一个是分界符,第二个是前缀符,第三个是结束符。也可以不传入参数Collectors.joining(),这样就是直接拼接。

四、总结

本篇主要从实际使用讲述了常用的方法及流,使用java8可以很清晰表达你要做什么,代码也很简洁。本篇例子主要是为了讲解较为简单,大家可以去使用java8重构自己现有的代码,自行领会lambda的奥妙。本文说的Stream要组合使用才会发挥更大的功能,链式调用很迷人,根据自己的业务去做吧。

文章来源:https://juejin.cn/post/6844903849753329678
对我有用,我要     转载  
文章分类: Java  
所属标签: lambda  
  • 0条评论
  • 只看作者
  • 按时间|按热度
  • 由于本人多次涉及需要打印这个证明,而每次都会忘记入口,在网上各种搜索各种摸索很是浪费时间。故本次将操作流程整理记录下来,以备忘。同时也分享给大家。1、打开湖北政务服务网,地址:http://zwfw.hubei.gov.cn/s/index.html2、切换区域到“武汉市”3、在“特色服务”模块找到“(个人)武汉市社会保险公共服务平台”4、进入“(个人)武汉市社会保险公共服务平台”,登录账号密码<imgsrc="https://cdnstatic.hoscen.cn/blog/article/184053017752895488/img/497065960be44747825acb86a17483c1.png"style=
  • 一般我们在使用CDN时都设置有缓存时间,当源站资源发生变更后,如果缓存时间没到,那么用户访问的依旧是变更前的数据,虽说又拍云控制台提供了缓存刷新功能界面,但是每次都手动去刷新显示不太理想,当然又拍云也想到了这一点,提供给我们有API可以调用。本篇文章就是讲解如何接入又拍云缓存刷新API。网站免费接入又拍云CDN的方法,请查看我另外一篇文章,地址: https://www.hoscen.cn/blog/hao/articles/204022774975430656.html又拍云API文档:https://api.upyun.com/doc#/api/guide/overview看完文档,我们会知道又拍云提供有两个缓存刷新接口,一个支持通配符(但次数有限),一个是完整url刷新。同时注意调用接口时将 Token 放入 HTTP Header 中 。那么我们需要3个接口:1、获取token2、URL刷新3、缓存批量刷新详细请求参数和响应值请查看文档。话不多说,我们直接放上核心代码1、获取token2、URL刷新<img src="https://cdnstatic.hoscen.cn/blog/article/
  • 1. 两数之和

    阅读数539
    总结:1、解决方法通常我们最容易想到的是暴力枚举(双重for循环),时间复杂度O(N^2),空间复杂度O(1),其中N是数组中的元素数量 2、利用哈希表可以以空间换时间,时间复杂度O(N),空间复杂度O(N),其中N是数组中的元素数量 题目给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:给定nums=[2,7,11,15],target=9因为nums[0]+nums[1]=2+7=9所以返回[0,1]答案第一版:暴力枚举,时间复杂度高public int[] twoSum(int[]nums, int target){   int len=nums.length;   
  • 生活中难免有觉得“好累啊”的时候,岁月在不同阶段将不同压力倾轧到不同人身上,那些时刻降临时,多半只能靠自己挺过去,“人应该有力量,揪着自己的头发把自己从泥地里拔起来。”  10种疲累,10个“解药”。给累了的你。《如果,你觉得很累很累……》No.1明明休了周末,周一上班还是觉得:好累啊……一剂解药:所谓的休息并不单纯只有躺下,而是做自己想做的事,不做自己不想做的事情。休息有“储存的休息”和“释放的休息”两种。“储存的休息”通过休息来储存体力和活力。“释放的休息”则通过做喜欢的事情来释放平日累积的郁闷和压力。“储存的休息”不足时身体会坏掉,“释放的休息”不足时精神会崩坏。开药者丨@bibibi_senseiNo.2总是活在别人的眼光里,总是被他人的评价所左右,好累啊……一剂解药:纵使被说坏话
  • 数据结构绪论

    阅读数537
    1、数据结构起源早期计算机被理解为数值计算工具,但是我们还有一些非数值的计算,因此需要一些更科学有效的手段(比如表、树、图等数据结构)的帮助来处理问题。所以数据结构是一门研究非数值计算的程序设计问题中的操作对象以及它们之间的关系和操作等相关问题的学科。2、基本概念和术语2.1 数据数据:是描述客观事物的符号,是计算机中可以操作的对象,是能够被计算机识别,并能输入给计算机处理的符号集合。数据不仅包括整型、实型等数值类型,还包括字符、声音、图像、视频等非数值类型。我们所说的数据,其实就是符号,但是这个符号需要满足两点:能输入到计算中能被计算机程序处理2.2 数据元素数据元素,也被称为记录。是组成数据的、具有一定意义的基本单位。比如,在人类中,人就是数据元素。在禽类中,猪狗牛羊等动物就是禽类的数据元素。2.3 数据项数据项:一个数据元素可以由若干个数据项组成。比如 人这个数据元素,就可以有眼、耳、鼻等数据项,也可以有姓名、年龄等数据项。具体有哪些数据项是视你所做的系统来决定。数据项是数据不可分割的最小单位。但注意真正讨论问题时,数据元素才是数据结构中建立数据模型的着眼点。就像我们讨论一个电影,通常是讨论角色这个“数据元素”,而不是讨论角色的姓名、年龄这些“数据项”。2.4 数据对象数据对象:是性
  • 什么是内卷?

    阅读数537
    最近一个词频频引起热议:内卷可能很多人看到都是一脸懵或者一脸冷漠:这啥玩意,跟我有什么关系?实际上,内卷与你的生活、工作、甚至未来都息息相关百度百科上的解释是:指一种社会某一发展阶段达到某种确定的形式之后,这种形式便停滞不前,难以转化为另一种高级模式的现象,从而把自我锁死在低水平状态上,周而复始地循环。这⼀概念最早是⽤来研究⽖哇的⽔稻农业。在殖⺠地时代和后殖⺠地时代的⽖哇,农⺠在⼈⼝压⼒下不断增加⽔稻种植过程中的劳动投⼊,以获得较⾼的产量。但实际上,当地农业⽣产⻓期原地不动,只是不断地重复简单再⽣产,不能提⾼单位⼈均产值。根据简单的经济学常识就知道,劳动的超密集投⼊,不会带来产出的成⽐例增⻓,⽽会导致单位劳动边际报酬递减。举一个咱们有代入感的例子在一家公司,所有员工都按时上下班,完成8小时的工作量。但突然有一个新员工A来了,他任劳任怨、自愿加班完成10小时的工作量,他的努力也获得了领导的欣赏与嘉奖。其他
  • 本文主要讲canonical标签的使用、canonical标签的作用、canonical标签SEO,在实践中如何正确规范的使用canonical标签。Canonical标签实际上就是一个页面内的301转向,可以帮助我们解决内容一样url不一样的网址规范化问题。和301跳转不同的是,用户并不被转向,但是对于搜索引擎来说,页面链接的权重是会被集中到代码中指明的规范化url上的。 图片来源:一灯出海对于经验丰富的SEO人员来说,canonical标签的使用一定不陌生,但最近在实践中发现不少网站的页面虽然用了canonical标签,但是使用方法却不规范。所以在这里和大家一起探讨一下canonical标签的规范使用方法,让更多的SEO人员避免走弯路。如果一个页面有多个url:https://www.hoscen.cn/blog/hao/articles/211896161185824768.htmlhttps://www.hoscen.cn/blog/hao/articles/211896161185824768/view这些url的页面内容完全一样,而我们想优化的规范化url为<ahref="https://www.hoscen.cn/blog/ha
  • 本文主要讲到PMP考试介绍、PMP考试试题分布、考题类型、PMI理念、答题技巧、学习方法与建议。PMP考试介绍1、它是笔试,200道选择题,都是单选题,四选一;2、不做选择,算答错。选了多个,也算错; 3、中英文对照,对于中国考生看中文就可以了,但是有些题的翻译不好,所以当读到题目有些别扭、或者觉得选项与题目都有点不符的时候,有些细节还是需要对照一下英文的; 4、200题里面有25题不计分,是PMI用来测试本次考试是否太难、太容易、或者争议非常大的题目。但是这25题并不知道是哪些,随机散乱的分布在试卷中。 5、所以200题的PMP考试,总分是200分,131及以上算是及格,我们对自己的要求当然是200分啦。 6、考试答题时间:9:00~13:00,共计4个小时。因为正式考试需要涂写答题卡,因此平时做题的时候,要控制时间,尽量在3小时内完成。 PMP考试试题分布过程组比例题目数量启动过程组13%26题规划过程组24%48题
  • SQL计算日期相差多少分钟,示例SELECT  ROUND(TO_NUMBER(to_date(rs.t_cap_wf_finReq_start_date,'YYYY/MM/DDhh24:mi:ss')-to_date(rs.t_cap_wf_start_date,'YYYY/MM/DDhh24:mi:ss'))*24*60) FROM table_xxx 更多差值单位写法天:ROUND(TO_NUMBER(END_DATE-START_DATE))小时:ROUND(TO_NUMBER(END_DATE-START_DATE)*24)分钟:ROUND(TO_NUMBER(END_DATE-START_DATE)*24*60)秒:ROUND(TO_NUMBER(END_DATE-START_DATE)*24*60*60)毫秒:ROUND(TO_NUMBER(END_DATE-START_DATE)*24*60*60*1000) 
  • 很多时候我们需要Linux服务器定时去运行一个脚本来触发一个操作,比如写缓存数据到硬盘、定时备份、定时重启服务、定期清除日志等。下面就简单讲解一下Linuxcrontab命令如何实现自动循环执行shell脚本。一、准备shell脚本比如我们准备一个hello.shvim/hcn/sh/hello.sh#!/bin/bash  DATETIME=$(date"+%Y%m%d%H%M%S") echo"hello, www.hoscen.cn,时间:${DATETIME}"  通过chmod命令赋予该脚本的执行权限chmod755hello.sh测试正确性二、开启crontab服务 linux应该都有crontab,没有的话可以安装一下:yuminstall vixie-cronyuminstall crontabsvixie-cron软