生命日历
<一个格子代表一周,寿命按90岁来算,一生有这么多的格子>
为了让自己的拖延症能够得到控制,做个生命日历一起提示自己吧 -QAQ-
Lambda表达式
函数式编程思想
函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
冗余的Runnable代码
举例我们需要一个线程去完成任务:
//面向对象思想
Runnable task = new Runnable() {
@Override
public void run() { // 覆盖重写抽象方法
System.out.println("多线程任务执行!");
}
};
new Thread(task).start(); // 启动线程
奔着Java一切皆对象思想,我们需要创建一个Runnable接口的匿名内部类对象来指定任务内容 ,在将其交给线程进行启动.
//Lambda表达式写法
new Thread(()->System.Out println("多线程任务执行了")).Start();
//
两个写法的执行结构一样的,Lambda表达式相对更加简便<只能简化匿名内部类的写法>
好处:
使得代码更简洁,可读性强
传递行为,而不止是传递值,更便于功能复用
流的并行化操作
2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。
Lambda表达式在后面学习的C++的STL算法库、python、ruby、scala等经常使用
看了些大佬写的Lambda入门,非常nice-------->大佬Lambda第一篇入门
第二个链接地址------------------->第二篇的入门
1.1 概念
Lambda表达式就是对匿名内部类对象的一种格式的简化。
强调的是做什么,而不是怎么做
语体结构相对简化,但Java内部编程应用普及程度不高
组成:
由3部分组成
1.参数列表 ()
2. 箭头 ->
3.语句体<方法体> {}
4种样式或简写格式
lambda表达式参数列表的参数类型 < int等 > 可以省略
如果Lambda体中语句只有一句,那么大括号可以省略
如果大括号中只有一条语句,并且是return语句<大括号同时省略>return可以省略
格式1:
无参数无返回值
() -> System.out.println(“格式1”);
格式2:
有参无返回
(x) -> System.out.println(“格式2”+x);
格式3:
有n参数,无返回
(n1, n2) -> System.out.println(“格式3”+n1+n2);
格式4:
有参,有返回语句体有多个语句
(n1, n2) -> {int result = n1 + n2; return result;}
有参有返回单条语句
(n1,n2)->n1+n2
常见的Lambda表达式
Runnable r = ()-> System.out.println("线程被执行了");
Consumer<String> con = out::println;//引用其他类中定义好的静态方法
Function<String,Integer> fun1 =Integer::parseInt; //把数字类型的字符串转换成数字
Predicate<Integer> p1 = (x) -> x>0; //大于0的正数
s = s.filter((x) -> x.startsWith("张")); //Stream流,取首个汉字为张的name
目标类型:
目标类型是函数式接口<接口只能有一个显示声明的抽象方法为函数式接口>
Lambda表达式使用的前提,就是接口必须是一个函数式接口。
函数式接口
只有一个抽象方法,那么这个接口就是函数式接口
注解: @FunctionalInterface
使用注解来检查当前接口是否是一个函数式接口
如果不是函数式接口,则编译报错。<注解可有可无>
常用函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在java.util.function包中被提供。
JDK 8 中多了个注解 @FunctionalInterface。这就是为何能在 JDK 8 中可以使用这种箭头式的 Lambda 写法。
函数式接口 | 参数类型 | 返回类型 | 说明 |
---|---|---|---|
Consumer< T >
消费型接口 | T | void | 对类型为T的对象操作,方法:void accept(T t) |
Supplier< T>
供给型接口 | 无 | T | 返回类型为T的对象,方法:T get();可用作工厂 |
Function<T, R>
函数型接口 | T | R | 对类型为T的对象操作,并返回结果是R类型的对象。方法:R apply(T t); |
Predicate< T>
断言型接口 | T | boolean | 判断类型为T的对象是否满足条件,并返回boolean 值。方法boolean test(T t); |
其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型为T,U参数应用操作,返回R类型的结果。包含方法为Rapply(Tt,Uu); |
UnaryOperator(Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为Tapply(Tt); |
BinaryOperator(BiFunction子接口) | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为Tapply(Tt1,Tt2); |
BiConsumer<T,U> | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(Tt,Uu) |
ToIntFunctionToLongFunctionToDoubleFunction | T | int,long,double | 分别计算int、long、double、值的函数 |
IntFunctionLongFunctionDoubleFunction | int,long,double | R | 参数分别为int、long、double类型的函数 |
消费型接口
抽象方法
void accept(T t);
有参无返回
作用:
当某个函数可以接收一个数据,并且处理这个数据,处理完成之后,不需要返回任何数据,这个函数需要当做数据来进行传递,就使用消费型接口。
示例:提供一个方法,最终打印花了多少钱,干了什么事?
package com.cn.day;
import java.util.function.Consumer;
public class A {
public static void main(String[] args) {
//这个接口提供一个方法能帮助我们打印做什么事
Consumer<String> con=(str)-> System.out.println(str);
test(500,"补课",con);
}
public static void test(int money, String str, Consumer<String> con){
System.out.println("花了"+money);
con.accept(str);
}
}
接口此处简化的内容就是Consumer接口中的accept方法;
Consumer<String> con = new Consumer<String>() {
public void accept(String str){
System.out.println(str);
}
};
方法引用
使用lambda格式实现一个函数式接口,如果接口方法已经被其他对象实现,就不需要再次调用这个实现,直接使用
使用格式如下:
- 函数式接口 名称 = 对象名 :: 方法名称
- 函数式接口 名称 = 类名 :: 静态方法名
比如:
Function<String,Integer> fun1 =Integer::parseInt;
作用
把已经实现的方法,作为一个数据,作为一个引用,赋值给某个函数式接口引用
可以把这个引用当做方法的返回值,也可以作为方法的实际参数进行传递
方法引用的三种方式实例展示:
class A2{
public static void main(String[] args) {
// Consumer<String > consumer=(x)-> System.out.println("Hello");
// consumer.accept("Hello");
//调用JDKSystem.Out对象已有的println方法
Consumer<String >consumer1=System.out::println;
consumer1.accept("Hello");
//调用自定义方法
A2 a2 = new A2();
Consumer<String> con2=a2::Mymethod1;
con2.accept("名字");
//调用自定义静态方法
int[] x={1,2,3,4};
Consumer<int[]>con3= A2::Mymethod2;
con3.accept(x);
}
public void Mymethod1(String str) {
str="杨小益";
System.out.println(str);
}
public static void Mymethod2(int[] arr) {
for (Integer integer : arr) {
System.out.println(integer);
}
}
}
供给型接口
抽象方法
T get()
无参有返回
使用 在若需要定义方法 这个方法不需要任何参数,返回需要的数据
作用
若需要定义函数,生成一个需要的数据,函数需要当成数据进行传递,可以使用供给型接口
示例:
class GJ{
public static void main(String[] args) {
/*
定义一个方法,返回一个集合,集合中n个满足要求的数据
要求数据是随机数字,要在1-10之间
* */
Supplier<Integer>sup=()->{
Random random = new Random();
int num=random.nextInt(10)+1;
return num;
};
ArrayList<Integer> arraylist = getArraylist(3, sup);
for (Integer integer : arraylist) {
System.out.println(integer);
}
}
private static ArrayList<Integer> getArraylist(int i, Supplier<Integer> sup) {
ArrayList<Integer> integers = new ArrayList<>();
for (int j = 0; j < i; j++) {
integers.add(sup.get());
}
return integers;
}
}
函数式接口
抽象方法
R apply(T t)
有参数,有返回值,传入一个对象T,返回对象R
andThen(Function f):在调用者处理方式之后,再进行参数的处理方式处理
作用:
如果需要定义一个函数,接收一个数据,将数据进行处理,完成之后,还能返回一个结果,就可以使用函数型接口。
示例:
需求:定义一个方法,传入一个String的变量, 把字符串转换成数字 并乘10后返回。
class HanShu{
// 需求:定义一个方法,传入一个String的变量,
// 把字符串转换成数字 并乘10后返回。
public static void main(String[] args) {
Function<String,Integer>function=(string)->
Integer.parseInt(string);
Integer i = getStringToInt("822", function);
System.out.println(i);
}
private static Integer getStringToInt(String s, Function<String, Integer> function) {
Integer apply = function.apply(s);
return apply;
}
}
断言型接口
抽象方法
boolean test(T t)
有参有返回
判断对象T,返回Boolean值
其他功能
Predicate and(Predicate pre):
在调用者条件判断之后,再由参数条件判断,返回两个条件的都满足的判断对象
Predicate or(Predicate pre):
返回两个条件任意一个满足的判断对象
Predicate negate():
返回的是调用者判断条件的取反
示例:
需求:下面的方法需要可以对Arravlist进行数据的过滤
比如:正数,负数等过滤方式
最终可以返回一个满足条件的Arrartist集合
class Duanyan{
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(66);
list.add(666);
list.add(35);
list.add(-20);
list.add(0);
list.add(33);
Predicate<Integer>p1=(x)->x>=0;
List<Integer> list1 = filterZhengshu(list,p1);
System.out.println(list1);
Predicate<Integer>p2=(x)->x<0;
List<Integer> list2=filterFushu(list,p2);
System.out.println(list2);
}
private static List<Integer> filterFushu(ArrayList<Integer> list, Predicate<Integer> p2) {
ArrayList<Integer> list1 = new ArrayList<>();
for (Integer integer : list) {
if (p2.test(integer)){
list1.add(integer);
}
}
return list1;
}
private static List<Integer> filterZhengshu(ArrayList<Integer> list, Predicate<Integer> p1) {
ArrayList<Integer> list1 = new ArrayList<>();
for (Integer integer : list) {
if(p1.test(integer)){
list1.add(integer);
}
}
return list1;
}
}
Stream流的并行化操作
过滤:filter
可以通过
filter
方法将一个流转换成另一个子集流。
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个
Predicate
函数式接口参数作为筛选条件。
统计个数:count
流提供
count
方法来数一数其中的元素个数:
long count();
该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。
取用前几个:limit
limit
方法可以对流进行截取,只取用前n个
Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
跳过前几个:skip
使用
skip
方法获取一个截取之后的新流:
Stream<T> skip(long n);
组合:concat
如果有两个流,希望合并成为一个流,那么可以使用
Stream
接口的静态方法concat
:
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
备注:这是一个静态方法,与
java.lang.String
当中的concat
方法是不同的。
逐一处理:forEach
将流中的数据,根据Consumer描述的处理方式,进行处理。
void forEach(Consumer<? super T> action);
该方法接收一个Consumer
接口函数,会将每一个流元素交给该函数进行处理
stream可以对数据进行过滤
比不断的自定义循环,要简单很多。
public class Demo2 {
public static void main(String[] args) {
//Collection单列集合的获取 直接使用对象的stream()方法获取即可
HashSet<String> set = new HashSet<>();
Stream<String> s = set.stream();
//Map的获取 先把其转换成单列集合在通过stram()获取流对象
HashMap<String, String> map = new HashMap<>();
//对KEY过滤
Set<String> ss = map.keySet();
Stream<String> s1 = ss.stream();
//对Value过滤
Collection<String> values = map.values();
Stream<String> s3 = values.stream();
//对entry项过滤
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> s4 = entries.stream();
//数组的获取 Stream中有一个of的静态方法
int[] arr = {1,2,3,4,5};
Stream<int[]> s5 = Stream.of(arr);
}
}