函数式编程

什么是Lambda表达式

可以理解为一种匿名函数的代替,lambda允许将函数作为一个方法的参数传递,从而简化代码编写。

什么是函数式接口

lambda表达式需要函数式接口的支持

所谓函数式接口,是指只有一个抽象方法的接口

在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成"非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这个注解是非必须的,这样提醒别人避免在里面添加新的接口函数了。

Lambda表达式的基本语法

函数式接口  变量名  = (参数1,参数2...)->{

方法体

}

传递行为参数

public class Demo4 {public static void main(String[] args) {Student s = new Student();//接口引用指向实现类对象TravelMode ridingMode = new RidingMode();//利用多态调用了实现类的方法//这个方法调用的本质是ridingMode.getToSchools.attendSchool(ridingMode);}
}class Student{//传递一个接口类型的参数对象,这个接口类型的参数对象只有一个方法public void attendSchool(TravelMode travelMode){travelMode.getToSchool();}
}interface TravelMode{public abstract void getToSchool();
}class WalkMode implements TravelMode{public void getToSchool(){System.out.println("步行");}
}class RidingMode implements TravelMode{public void getToSchool(){System.out.println("骑车");}
}

 代码简述:

目的是想在学生的上学方式的方法中执行代码时有不同的输出结果,这时候知道需要利用多态
多态可以是父类指向子类,可以是接口指向实现类,可以是抽象类指向子类
这里用了函数式接口,即接口指向实现类,定义了一个接口和三个实现类,实现不同的方式
以接口类型为传入参数类型,就可以传入不同的实现类了
注意父类接口类型只能传入实现类,或者父类本类,但接口用new 接口名的形式就是创建了匿名内部类,子类参数类型
不能传入父类对象,猫是动物,但动物不一定是猫
不同的实现类代表这不同的输出结果

课本lambda表达式引入

public class Demo4 {public static void main(String[] args) {Student s = new Student();//接口引用指向实现类对象TravelMode ridingMode = new RidingMode();//利用多态调用了实现类的方法//这个方法调用的本质是ridingMode.getToSchools.attendSchool(ridingMode);//搭顺风车/*搭顺风车在实际中可能不经常出现,但要添加这种方式的话传统方式就需要添加一个类这个代码可能就执行一次,所以没有创建一个类的必要,为此可以创建匿名内部类,直接创建了这个接口的一个实现类,而写不用写这个实现类的类型,写了接口类型的名字所以叫匿名内部类,=后面的就相当于这个实现类需要实现的方法,以前是接口类型引用变量-实现类-实现类的方法,这种模式串到一起,现在直接将接口类型引用变量-实现类方法绑到一起,不用创建额外创建实现类了*/TravelMode freeRide = new TravelMode() {@Overridepublic void getToSchool() {System.out.println("搭顺风车");}};s.attendSchool(freeRide);//匿名内部类的代码简化->lambda表达式TravelMode freeRide1 = ()->{System.out.println("搭顺风车");};s.attendSchool(freeRide1);}
}class Student{//传递一个接口类型的参数对象,这个接口类型的参数对象只有一个方法public void attendSchool(TravelMode travelMode){travelMode.getToSchool();}
}interface TravelMode{public abstract void getToSchool();
}class WalkMode implements TravelMode{public void getToSchool(){System.out.println("步行");}
}class RidingMode implements TravelMode{public void getToSchool(){System.out.println("骑车");}
}

 代码简述:注意看注释中的解释,新添加了匿名内部类和lambda表达式

public class Demo4 {public static void main(String[] args) {Student s = new Student();//接口引用指向实现类对象TravelMode ridingMode = new RidingMode();//利用多态调用了实现类的方法//这个方法调用的本质是ridingMode.getToSchools.attendSchool(ridingMode);//搭顺风车/*搭顺风车在实际中可能不经常出现,但要添加这种方式的话传统方式就需要添加一个类这个代码可能就执行一次,所以没有创建一个类的必要,为此可以创建匿名内部类,直接创建了这个接口的一个实现类,而写不用写这个实现类的类型,写了接口类型的名字所以叫匿名内部类,=后面的就相当于这个实现类需要实现的方法,以前是接口类型引用变量-实现类-实现类的方法,这种模式串到一起,现在直接将接口类型引用变量-实现类方法绑到一起,不用创建额外创建实现类了*/TravelMode freeRide = new TravelMode() {@Overridepublic void getToSchool() {System.out.println("搭顺风车");}};s.attendSchool(freeRide);//匿名内部类的代码简化->lambda表达式TravelMode freeRide1 = ()->{System.out.println("搭顺风车");};s.attendSchool(freeRide1);/*将lambda表达式横着写TravelMode freeRide1 = ()->System.out.println("搭顺风车");可以这样类比:函数式接口TravelMode相当于数据类型行为变量freeRide1相当于数据变量Lambda表达式()->System.out.println("搭顺风车") 相当于数据值因此lambda表达式除了可以作为参数值还可以作为返回值*/Teacher t = new Teacher();TravelMode freeRide2 = t.giveConvenience();s.attendSchool(freeRide2);//或者s.attendSchool(t.giveConvenience());}
}
class Teacher{public TravelMode giveConvenience(){return ()-> System.out.println("搭顺风车");}
}
class Student{//传递一个接口类型的参数对象,这个接口类型的参数对象只有一个方法public void attendSchool(TravelMode travelMode){travelMode.getToSchool();}
}interface TravelMode{public abstract void getToSchool();
}class WalkMode implements TravelMode{public void getToSchool(){System.out.println("步行");}
}class RidingMode implements TravelMode{public void getToSchool(){System.out.println("骑车");}
}

代码简述:新添加了老师类和lambda作为返回值的方式,注意看注释解释

 lambda应用实例1

public class Demo {public static void main(String[] args) {Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("run");}};new Thread(runnable).start();/*()里面写参数,这里替代了匿名内部类,在匿名内部类中,变化的部分只有方法体所以这里面就只写方法体*///第一种方式,主要内容就只有参数列表和方法体了Runnable runnable1 = ()->{System.out.println("hello");};new Thread(runnable1).start();//第二种方式new Thread(()->{System.out.println("hello xz");}).start();//第三种方式,还可以更省,只有一条语句时,{}也可以省去new Thread(()-> System.out.println("hi ")).start();}
}

代码简述:lambda基本语法

lambda应用实例2

public class Demo2 {public static void main(String[] args) {//传统方式用匿名内部类创建比较器Comparator <String>comparator = new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.length()-o2.length();}};TreeSet<String> treeSet = new TreeSet<>(comparator);//lambda表达式,这里o1,o2也不用写类型了,写类型是多余的,因为在前面泛型里面写了StringComparator<String> comparator1 = (o1,o2)-> o1.length()-o2.length();TreeSet<String> treeSet1 = new TreeSet<>(comparator1);//利用lambda表达式的更简单写法TreeSet<String> treeSet2 = new TreeSet<>((o1,o2)->o1.length()-o2.length());/*以前new 接口名/抽象类名(){方法体}代表一个接口的实现类或者一个抽象类的子类现在(方法体参数)->{方法体}用来代表一个接口的实现类或者一个抽象类的子类,只包含方法的部分,就像直接传入了一个方法*/}
}

lambda表达式的作用

Lambda结合FunctionalInterface Lib, forEach, stream(),method reference等新特性可以使代码变的更加简洁!

lambda表达式总结:

lambda引入了新的操作符:->将表达式分为了两个部分

左侧:(参数1,参数2...)表示方法的参数列表

右侧:{}内部是方法体

1.形参列表的数据类型会自动判断,因为有泛型指明

2.如果形参列表为空,只保留()

3.如果形参只有一个,()可以省略,只需要参数的名称即可

4.如果执行语句只有一句,则无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句保证只有一句

5.lambda不会生成一个单独的内部类文件

6.lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后修改局部变量会报错,这一点匿名内部类的使用规则

转载一下这一篇文章更有助于自己理解

Java8 Lambda 表达式有何用处?如何使用?_weixin_33957648的博客-CSDN博客

Java8提供的函数式接口

Predicate

用于做判断,输入一个对象,得到布尔结果,下面是一个判断考试是否通过的例子

class ExamJudgementDemo{public static void main(String[] args) {//在()定义了score,判断score是否大于60Predicate<Integer> passExam = (Integer score) -> score>=60;//通过passExam.test(),并且传递了一个参数,小于60会输出未通过,反之通过System.out.println("考试"+(passExam.test(59)?"通过":"未通过"));}
}

Consumer

用于处理数据,输入一个对象,没有返回值

借鉴一下他人文章方便复习

常用的函数式接口_Consumer_weiyoo55的博客-CSDN博客_函数式接口consumer

Supplier

用于创建对象,没有输入,返回对象

这个接口Supplier<T>中只有一个无参的方法:T.get();也就是说传入接口的类型就是返回类型

常用的函数式接口_Supplier_weiyoo55的博客-CSDN博客

Function

用于映射数据,输入一个对象,返回另一个对象,将数据的数据类型进行转换

Function(T,R)前者称为前置条件,后者称为后置条件,将T类型转为R类型

抽象方法:R apply(T t)

常用函数式接口_Function_weiyoo55的博客-CSDN博客

流式编程

什么是流水线操作

streamAPI的特点是像流水线一样操作集合,严格区分执行顺序,前一个操作的结果是后一个操作的原料,上图中起连接作用的箭头就是流

在这里最后一个流没有箭头,流在这里终止了,根据这个特点,流水线操作被分为两大类,中间操作和终端操作

中间操作操作输出流,可以继续连接其他操作,但不能输出结果

终端操作输出结果,但流被终止了不能再继续其他操作

在Java.util.stream包中的流水线操作的接口

操作 类型 作用
filter 中间操作 筛选
sorted 中间操作 排序
skip,limit 中间操作 分页
map 中间操作 映射
collect 终端操作 转换成集合
reduce 终端操作 数据收集

 流水线操作,学校筛选学生参加编程竞赛,熟悉stream流的操作

前提

先把集合转换成流,语法:Stream<T> 集合流 = 集合.stream;

public class Demo7 {public static void main(String[] args) {TranscriptManager transcriptManager = new TranscriptManager();transcriptManager.addTranscript("1","xiaozhou",80);transcriptManager.addTranscript("2","xiaocheng",85);transcriptManager.addTranscript("3","xiaogou",75);transcriptManager.addTranscript("4","xiaoding",95);transcriptManager.addTranscript("5","xiaozhu",65);transcriptManager.addTranscript("6","xiaoma",70);//System.out.println(transcriptManager.allTranscripts);//System.out.println(transcriptManager.allTranscripts.size());System.out.println(transcriptManager.getCompetitor());System.out.println(transcriptManager.getAverageScoreForCompetitor());}
}class Transcript{private String studentID;private String studentName;private Integer score;Transcript(String id,String name,Integer score){this.studentID = id;this.studentName = name;this.score = score;}@Overridepublic String toString() {return "Transcript{" +"studentID='" + studentID + '\'' +", studentName='" + studentName + '\'' +", score=" + score +'}';}public String getStudentID() {return studentID;}public void setStudentID(String studentID) {this.studentID = studentID;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public Integer getScore() {return score;}public void setScore(Integer score) {this.score = score;}
}class TranscriptManager{Set<Transcript> allTranscripts = new HashSet<Transcript>();//收集成绩单public void addTranscript(String id,String name,Integer score) {//将各个学生的成绩单对象收集到集合里面this.allTranscripts.add(new Transcript(id, name, score));}public List<String> getCompetitor () {//执行:筛选,排序,分页,取出名字和学号//将收集各个成绩单的集合转换成流Stream<Transcript> transcriptStream = this.allTranscripts.stream();//使用filter()实现筛选/*filter在包下的定义Stream <T> filter(Predicate<? super T> predicate)传入参数的类型是predicate的函数式接口,可以传入一个lambda表达式*///transcriptStream.filter(((Transcript t)->t.getScore()>=80));//使用sorted实现排序/*sorted在包下的定义Stream<T> sorted (Comparator<? super T> comparator)传入参数类型是Comparator的函数式接口,可以传入一个lambda表达式下面对刚才进行筛选国的成绩单进行排序*//*transcriptStream.filter((Transcript t)->t.getScore()>=80).sorted((Transcript t1,Transcript t2)->t1.getScore()-t2.getScore());*///使用skip和limit实现分页/*skip()用于跳过前m个元素,limit()用于取出连续的n个元素它们在包中的定义Stream<T> skip(long n)Stream<T> limit(long maxsize)传入参数是为long的整数,分别指定要跳过和取出的元素数量下面取第11名到50名的成绩单*//*transcriptStream.filter((Transcript t)->t.getScore()>=80).sorted((Transcript t1,Transcript t2)->t1.getScore()-t2.getScore()).skip(10).limit(40);*///map()实现映射/*map()在包中的定义<R> Stream<R> map(Function<? super T,?extends R> mapper);传入参数类型是为Function的函数式接口,可以传入lambda表达式下面将处理完的成绩单映射成对应学生的姓名和学号*//*transcriptStream.filter((Transcript t)->t.getScore()>=80).sorted((Transcript t1,Transcript t2)->t1.getScore()-t2.getScore()).skip(10).limit(40).map((Transcript t)->t.getStudentName()+":" + t.getStudentID());*///collect()获得结果/*collect()方法将流转成集合,以获得结果,理解为加工环节完成,可以下生产线出产品了在包中的定义<R,A>R collect(Collector<? super T,A,R> collcrtor;传入参数类型是COllector类型的函数式接口,可以传入Collectors类的静态方法常用方法Collectors.toList()Collectors.toSet()Collectors.toMap()*/List<String> competitorList=  transcriptStream.filter((Transcript t) -> t.getScore() >= 80).sorted((Transcript t1, Transcript t2) -> t1.getScore() - t2.getScore()).skip(0).limit(3).map((Transcript t) -> t.getStudentName() + ":" + t.getStudentID()).collect(Collectors.toList());return competitorList;}public int getAverageScoreForCompetitor () {//执行:筛选,排序,分页,计算平均分//这里介绍reduce()实现统计/*实现数据收集,指集合中任意两个元素做何种数学运算,这里要算出参赛者平均分要先计算总分,再除参赛者人数reduce() 第一个参数是收集器的初始值,第二个行为参数描述了收集方式:集合中的元素两两相加*/Stream<Transcript> transcriptStream = this.allTranscripts.stream();int total = transcriptStream.filter((Transcript t) -> t.getScore() >= 80).sorted((Transcript t1, Transcript t2) -> t1.getScore() - t2.getScore()).skip(0).limit(3).map((Transcript t) -> t.getScore()).reduce(0, (Integer a, Integer b) -> a + b);int count = this.getCompetitor().size();int average = total / count;return average;}}

 代码简述:看注释看注释!

使用并行流发挥多核优势

Stream API提供parallel()方法将串行流转换为并行流

串行是指集合中元素要排队逐个操作

并行是指集合中的元素可以同时操作,显然并行操作比串行操作花费的时间更少,但并行能力受到CPU核心数量的限制

除了计算机底层实现的区别,并行流和串行流在使用上没有任何分别

语法:Stream<T>并行流 = 串行流.parallel();

之前的成绩单对象转为流的方式:

Stream.<Transcript> transcriptStream = this.allTranscripts.stream().parallel();

其他部分的代码都无需变化,Java会自动利用cpu的多核心优势,使程序得到提高。


以上是课本内容的整理,以下是网上学习内容整理


什么是Stream

Stream是Java8中处理数组,集合的抽象概念,它可以指定你希望的集合进行的操作,可以执行非常复杂的查找,过滤映射数据等操作,使用Stream对集合数据进行操作,就类似于使用SQL执行的数据查询

Stream的特点

1.Stream自己不会存储元素

2.Stream不会改变源对象,相反他们会返回一个持有结果的新Stream

3.Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行

Stream遵循“做什么,而不是怎么去做”的原则,只需要描述还需要做什么,而不需要考虑程序怎样实现

//传统方式,查询集合中大于3的字符串的个数List<String> list = new ArrayList<>();list.add("a");list.add("baaaaa");list.add("cccccc");int count = 0;for(String s:list){if(s.length()>3){count++;}}System.out.println(count);//流式编程(链式编程)//Stream的方式,用stream去查询,filter过滤,s代表每一个对象,长度大于3的对象就数一次//s->s.length()>3是lambda表达式//stream和filter都是Stream接口类型long count1 = list.stream().filter(s -> s.length() > 3).count();System.out.println(count1);

使用Stream的步骤

1.创建一个Stream (创建)

2.在一个或多个步骤中,将初始Stream的中间操作,如上面例子中stream().filter (中间操作)

3.使用一个终止操作来产生一个结果,该操作会强制它之前的参翅操作立即执行,在这之后,该Stream就不会再被使用了(终止操作)

创建Stream的方式

JavaSE-Lambada相关推荐

  1. Lambada表达式的用法

    Lambada表达式是JDK1.8中最重要的新功能之一.使用Lambada表达式可以替代只有一个抽象函数的接口实现,告别匿名内部类.代码看起来更简洁易懂.Lambada表达式同时还提升了对集合.框架的 ...

  2. 【JavaSE】day03_Date、SimpleDateFormat、Calendar、Collection

    [JavaSE]day03_Date.SimpleDateFormat.Calendar.Collection 1.Date及其经常使用API 1)JAVA 中的时间 Java中的时间使用标准类库的D ...

  3. java视频为什么这么多_为什么看java教学视频教的都是javase,两者难道语言相同吗?...

    Java 分类 Java SE(Java Platform Standard Edition) :Java平台标准版.主要用于桌面应用程序的开发,是Java技术的核心,提供基础 Java开发工具.执行 ...

  4. 简述JavaME,JavaSE,JavaEE

    javaME:微型版,应用于移动等 JavaSE:标准版,应用于桌面环境 JavaEE:企业版,应用于基于Java的应用服务器 Java SE(Java Platform,Standard Editi ...

  5. javaee, javaweb和javase的区别以及各自的知识体系

    javaee, javaweb和javase的区别以及各自的知识体系 来源 https://blog.csdn.net/weixin_39297312/article/details/79454642 ...

  6. Java 基础入门随笔(1) JavaSE版——java语言三种技术架构

    1.java语言的三种技术架构: J2SE(java 2 Platform Standard Edition):标准版,是为开发普通桌面和商务应用程序提供的解决方案.该技术体系是其他两者的基础,可以完 ...

  7. javaSE基础04

    javaSE基础04 一.三木运算符 <表达式1> ? <表达式2> : <表达式3> "?"运算符的含义是: 先求表达式1的值, 如果为真, ...

  8. 零基础学习JavaSE(一)

    一.开发环境安装配置 1.1 安装jdk jdk下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 下载后安 ...

  9. javase基础复习攻略《三》

    编程语言的发展: 机器语言--直接由计算机的指令组成,指令.地址.数据都是以"0"和"1"的符号串组成,可以被计算机直接执行. 汇编语言--用容易理解和记忆的符 ...

  10. javase基础socket编程之局域网聊天,局域网文件共享

    2017年06月04日  23点25分 javase基础学完可以做什么,javase实现局域网聊天室 包含内容:基础语法,面向对象,多线程,IO流,GUI编程,网络编程(udp) 实现功能:局域网群聊 ...

最新文章

  1. Android常见错误
  2. 二、Windows下TortoiseGit的安装与配置
  3. electron 读取文件夹内容_如何使用Electron Framework选择,读取,保存,删除或创建文件...
  4. Node.js对MongoDB进行增删改查操作
  5. 当不同公司的产品经理在一块聊天,会聊什么?
  6. 在gluster中配置distributed 卷
  7. 动手学深度学习Pytorch Task03
  8. Java第六次作业--异常处理和Java类集
  9. “ 鸡尾酒会问题”(cocktail party problem)
  10. Python基础之变量和常量
  11. Label Matrix v8 怎样做二次开发
  12. 我与我的专业计算机作文500字,我和学校作文500字(精选5篇)
  13. mysql 本月老客户次月留存率_用mysql计算用户留存率
  14. 为什么你做数据分析没思路?
  15. 如何在Visual Studio中安装.net6.0 或者 将在Visual Studio中将.net 5.0更新为6.0
  16. OPA 论坛为流程控制设备接口扩展了开放性、互操作性标准 - 第一部分
  17. 3ds Max: Advanced Materials 3DS Max 教程之高级材质 Lynda课程中文字幕
  18. type struct 和struct的区别
  19. 动态规划初识(从dfs到dfs优化到动态规划顺推和逆推)
  20. python画结节图像_天池医疗AI大赛[第一季]:肺部结节U-Net图像分割

热门文章

  1. 《深入解析Windows操作系统》安全性
  2. 115846-45-2、二肽标记肽Suc-GP-对硝基苯胺、 Suc-Gly-Pro-pNA
  3. 网络组建大全(包括企业网、校园网、网吧和基础知识)(转)
  4. 04-前端技术_盒子模型与页面布局
  5. MATLAB中对矩阵的变换
  6. 百度主动推送怎么用?
  7. 用google突破图书馆入口IP限制小技巧
  8. 遨博机械臂——末端工具ROS驱动
  9. 公钥和私钥的完全解释(包括对称算法和非对称算法、RSA解释)
  10. SPSS--回归-多元线性回归模型案例解析!(一)