目录
1. Lambda
1.1 概述
Lambda 表达式是一种没有名字的函数,也可称为闭包,是Java 8 发布的最重要新特性。
本质上是一段匿名内部类 , 也可以是一段可以传递的代码
还有叫箭头函数的....
1.2 为什么使用Lambda表达式
Lambda表达式就是一个匿名内部类的简写方式
使程序更加简介清晰,编程效率也得到了提高
1.3 和匿名内部类对比
package _01_Lambda;
public class _01_Lambda {
public static void main(String[] args) {
int[] arr = {1,2,3,4};
//常规遍历
for(int j: arr){
System.out.println(j);
}
//1. 实现类
forEach(arr , new A());
//2. 匿名内部类
forEach(arr, new Array(){
@Override
public void m1(int i ){
System.out.println(i+"=========");
}
});
//3. Lambda 表达式
forEach(arr , (i)->System.out.println(i+1+"000000000"));
}
//封装的功能,完成遍历操作,但是需要传递数组,和要做的事情
private static void forEach(int[] arr, Array array) {
for(int i :arr){
array.m1(i);
}
}
}
//封装要做的事
interface Array{
void m1(int i );
}
class A implements Array {
@Override
public void m1(int i) {
System.out.println(i+"___________");
}
}
1.4 语法结构
可选类型声明 : 不需要声明参数类型 , 编译器可以统一识别参数值
可选的参数圆括号 : 一个参数无需定义圆括号,但多个参数需要定义圆括号
可选的大括号 : 如果主体包含了一个语句,就不需要使用大括号
如果不写{} return 不能写 分号不能写
可选的返回关键字 : 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明确表达式返回了一个值
如果只有一条语句 ,并且是返回值语句,就可以不写 return 不写{}
如果写上{} 就必须写return 和 ; 分号
如果有 多条语句, 必须写{} return 和 ; 也必须写
1.5 案例
1. 不需要参数 ,返回值为5
()->5
2. 接受一个参数(数字类型),返回其2倍的值
x->x*2
3. 接收2个参数(数字),并返回他们的差值
(x,y)->x-y
1.6 练习
package _01_Lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class _02_Lambda {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(22);
list.add(3);
//遍历
//常规写法
for(Integer integer : list){
System.out.println(integer);
}
//匿名内部类写法
list.forEach(new Consumer<Integer>(){
@Override
public void accept(Integer t){
System.out.println(t);
}
});
//Lambda 写法
list.forEach(q-> System.out.println(q));
}
}
package _01_Lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class _03_Lambda {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(22);
list.add(711);
//Lambda 排序 降序排序
Collections.sort(list, (x,y)->y-x);
System.out.println(list);
//传统的排序
Collections.sort(list , new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
System.out.println(list);
}
}
class B{
@Override
public boolean equals(Object obj){
return false;
}
}
2. 函数式接口
2.1 介绍
英文称为Functional Interface
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
核心目标是为了给Lambda表达式的使用提供更好的支持,进一步达到函数式编程的目标,可通过运用函数式编程极大地提高编程效率。
其可以被隐式转换为 lambda 表达式。
2.2 特点
函数式接口是仅制定一个抽象方法的接口
可以包含一个或多个静态或more方法
专用注解即@FunctionalInterface 检查它是否是一个函数式接口,也可不添加该注解
如果有两个或以上 抽象方法,就不能当成函数式接口去使用,也不能添加@FunctionalInterface这个注解
如果只有一个抽象方法,那么@FunctionalInterface注解 加不加 都可以当做函数式接口去使用
2.3 代码实现
2.3.1 无参情况
public class FunInterface_01 {
// 自定义静态方法,接收接口对象
public static void call(MyFunctionInter func) {
// 调用接口内的成员方法
func.printMessage();
}
public static void main(String[] args) {
// 第一种调用 : 直接调用自定义call方法,传入函数
FunInterface_01.call(() -> {
System.out.println("HelloWorld!!!");
});
// 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象
MyFunctionInter inter = () -> {
System.out.println("HelloWorld2!!!!");
};
// 调用这个实现的方法
inter.printMessage();
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionInter {
void printMessage();
}
2.3.2 有参情况
public class FunInterface_02 {
// 自定义静态方法,接收接口对象
public static void call(MyFunctionInter_02 func, String message) {
// 调用接口内的成员方法
func.printMessage(message);
}
public static void main(String[] args) {
// 调用需要传递的数据
String message = "有参函数式接口调用!!!";
// 第一种调用 : 直接调用自定义call方法,传入函数,并传入数据
FunInterface_02.call((str) -> {
System.out.println(str);
}, message);
// 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象
MyFunctionInter_02 inter = (str) -> {
System.out.println(str);
};
// 调用这个实现的方法
inter.printMessage(message);
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionInter_02 {
void printMessage(String message);
}
2.4 JDK自带常用的函数式接口
2.4.1Supplier<T> 接口
Supplier<T>接口 代表结果供应商,所以有返回值,可以获取数据
有一个get方法,用于获取数据
public class _03_JdkOwn_01 {
private static String getResult(Supplier<String> function) {
return function.get();
}
public static void main(String[] args) {
// 1
String before = "张三";
String after = "你好";
// 把两个字符串拼接起来
System.out.println(getResult(() -> before + after));
// 2 //创建Supplier容器,声明为_03_JdkOwn类型
// 此时并不会调用对象的构造方法,即不会创建对象
Supplier<_03_JdkOwn_01> sup = _03_JdkOwn_01::new;
_03_JdkOwn_01 jo1 = sup.get();
_03_JdkOwn_01 jo2 = sup.get();
}
public _03_JdkOwn_01() {
System.out.println("构造方法执行了");
}
}
package _02_Functions;
import java.util.function.Supplier;
/**
* Supplier : 代表供应商,有返回值,可以获取数据使用
*
* 提供一个get方法
*
* @author 人间失格
* @data 2021年11月2日下午7:08:42
*/
public class _02_Functions {
public static void main(String[] args) {
String string = "张三";
String result = getResult(()->"我是 :"+ string);
System.out.println(result);
}
// f封装 完成指定功能
private static String getResult(Supplier<String>supplier) {
return supplier.get();
}
}
2.4.2Consumer<T> 接口
Consumer<T> 接口 消费者接口所以不需要返回值
有一个accept(T)方法,用于执行消费操作,可以对给定的参数T 做任意操作
public class _04_JdkOwn_02 {
private static void consumeResult(Consumer<String> function, String message) {
function.accept(message);
}
public static void main(String[] args) {
// 传递的参数
String message = "消费一些内容!!!";
// 调用方法
consumeResult(result -> {
System.out.println(result);
}, message);
}
}
package _02_Functions;
import java.util.function.Consumer;
/**
* Consumer : 代表了消费者,提供了accept方法 有参数 但是无返回值
*
* @author 人间失格
* @data 2021年11月2日下午6:56:08
*/
public class _01_Functions {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, };
// 常规遍历
for (int j : arr) {
System.out.println(j);
}
// 3. lambda 表达式
forEach(arr , (i)->System.out.println(i));
}
//封装的功能,完成遍历操作,但是需要传递数组,和要做的事
//该方法只是帮我们遍历而已
public static void forEach(int[] arr , Consumer<Integer> c){
for(int i : arr){
c.accept(i);
}
}
}
2.4.3 FUnction<T,R> 接口
Function<T,R> 接口 , 表示接收一个参数并产生结果的函数
顾名思义,是函数操作的
有一个Rapply(T)方法,Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式
public class _05_JdkOwn_03 {
// Function<参数, 返回值>
public static void convertType(Function<String, Integer> function,
String str) {
int num = function.apply(str);
System.out.println(num);
}
public static void main(String[] args) {
// 传递的参数
String str = "123";
// s是说明需要传递参数, 也可以写 (s)
convertType(s -> {
int sInt = Integer.parseInt(s);
return sInt;
}, str);
}
}
package _02_Functions;
import java.util.function.Function;
/**
* Function : 有参有返回值,提供了apply方法
*
* @author 人间失格
* @data 2021年11月2日下午7:21:09
*/
public class _03_Functions {
public static Integer m1(String string, Function<String, Integer>fun) {
return fun.apply(string);
}
public static void main(String[] args) {
String string = "123";
int result = m1(string , s->Integer.parseInt(s));
System.out.println(result);
}
}
2.4.4 Predicate<T> 接口
public class _06_JdkOwn_04 {
// 自定义方法,并且 Predicate 接收String字符串类型
public static void call(Predicate<String> predicate, String isOKMessage) {
boolean isOK = predicate.test(isOKMessage);
System.out.println("isOK吗:" + isOK);
}
public static void main(String[] args) {
// 传入的参数
String input = "ok";
call((String message) -> {
// 不区分大小写比较,是ok就返回true,否则返回false
if (message.equalsIgnoreCase("ok")) {
return true;
}
return false;
}, input);
}
}
3. 方法引用和构造器调用
3.1对象调用成员
package _03_FunCall;
import java.util.function.Supplier;
/**
* 对象引用 :: 成员方法名
*
* @author 人间失格
* @data 2021年11月2日下午7:26:41
*/
public class _01_FunCall {
public static void main(String[] args) {
Integer i1 = new Integer(123);
//常规lambda写法
Supplier<String>su = ()-> i1.toString();
System.out.println(su.get());
//方法引用写法
su = i1 :: toString;
System.out.println(su.get());
}
}
3.2类名调用成员
package _03_FunCall;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* 类名 :: 静态
*
* @author 人间失格
* @data 2021年11月2日下午7:33:41
*/
public class _02_FunCall {
public static void main(String[] args) {
Function<String , Integer> fun = Integer:: parseInt;
System.out.println(fun.apply("123"));
fun = new Function<String , Integer>(){
@Override
public Integer apply(String t) {
return null;
}
};
fun = x-> Integer.parseInt(x);
fun = Integer::parseInt;
//前面两个是入参 , 第三个是返回值
//类名:: 静态
BiFunction<Integer , Integer , Integer>bif = Integer :: max;
}
//静态方法
public static void test(A a){
}
}
//接口
interface A{
public void m1(B b);
}
class B{}
3.3类名调用成员
package _03_FunCall;
import java.util.function.BiPredicate;
/**
* 类名 :: 成员方法名
*
* @author 人间失格
* @data 2021年11月2日下午7:43:40
*/
public class _03_FunCall {
public static void main(String[] args) {
//判断字符串是否相等
BiPredicate<String ,String> bp = String::equals;
System.out.println(bp.test("abc", "abc"));
}
}
3.4构造方法调用
package _01_Lambda;
import java.util.function.Function;
import java.util.function.Supplier;
public class _04_Constryctarcakk {
public static void main(String[] args) {
//无参构造
Supplier<Object> objSi = Object :: new ;
System.out.println(objSi.get());
//有参构造
Function<String , Integer>function = Integer :: new;
//new Integer("123")
System.out.println(function.apply("123"));
}
}
3.5数组引用
package _01_Lambda;
/**
*
* 数组引用
*
*/
import java.util.function.Function;
public class _05_ArrayCall {
public static void main(String[] args) {
Function<Integer , Integer[]> function = Integer[] :: new;
Integer[] arr = function.apply(6);
for(Integer integer : arr){
System.out.println(integer);
}
}
}
4. StreamAPI
4.1 概念说明
数据渠道、管道 , 用于操作数据源(集合、数组等)所生成的元素序列
集合讲的是数据,流讲的是计算
即一组用来处理数组,集合的API
4.2特点
Stream 不是数据结构,没有内部存储,自己不会存储元素
Stream 不会改变源对象. 相反,他们会返回一个持有结果的新Stream
Stream 操作是延迟执行的.这意味着他们会等到需要结果的时候才执行
不支持索引访问
延迟计算
支持并行
很容易生成数据或集合
支持过滤, 查找 , 转换, 汇总, 聚合等操作
4.3应用场景
流式计算处理,需要延迟计算、更方便的并行计算
更灵活、简介的集合处理方式场景
4.4代码实现
4.4.1 运行机制说明
Stream分为源source , 中间操作,终止操作
流的源可以是一个数组,集合,生成器方法,I/O通道等等
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作
中间操作也称为算子-transformation
Stream 只有遇到终止操作,它的数据源会开始执行遍历操作
终止操作也称为动作算子-action
因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
4.4.2创建流的方式
package _04_Stream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 生成流
*
* @author 人间失格
* @data 2021年11月2日下午7:46:11
*/
public class _01_Stream {
public static void main(String[] args) {
//1. 数组转换位流
String[] strings = {"q","w","e","r"};
Stream<String> streaml = Stream.of(strings);
System.out.println(streaml);
//java.util.stream.ReferencePipeline$Head@6d06d69c
//2. 通过集合
//Arrays.asList : 把数组转换位集合
List<String>list = Arrays.asList(strings);
streaml=list.stream();
//3. 通过Stream的generate创建
//但是是一个无限流(无限大), 所以经常结合limit一起使用,来限制最大个数
//generate参数是一个Supplier , 而Supplier中有一个get方法用于获取数据
//而get方法的返回值,会作为数据保存到这个流中,下面程序也就意味着该流中的数据全部都是1
Stream<Integer>stream2 = Stream.generate(()->1);
//limit 是中间操作,设置流的最大个数
//forEach 遍历 ,是终止操作
stream2.limit(5).forEach(x->System.out.println(x));
// 4. 通过String.iterate
//同上,是无限流
//参数1 是起始值 ,参数二是function , 有参有返回值
//x+2 等于 步长位二 for (int i =1; true ; i+=2)
Stream<Integer>streams = Stream.iterate(1, x->x+2);
streams.limit(10).forEach(x->System.out.println(x));
// 5 . 已有类的API
String string = "abc";
//string转为char类型 输出值位对应的ASCII值
IntStream chars = string.chars();
chars.forEach(x->System.out.println(x));
}
}
4.4.3 常见中间操作
4.4.3.1概述
一个中间操作链,对数据进行处理,一个流可以有0~N个中间操作
他们每一个都返回新的流,方便下一个进行操作
但是只能有一个终止操作
4.4.3.2 常见中间操作
* fitter : 对元素进行过滤筛选,不符合的就不要了
*
* distinct : 去掉重复元素
*
* skip :跳过多少元素
*
* limit :取最大条数(前几条)
*
* map : 对集合中的元素进行遍历操作
*
* sorted : 排序
4.4.3.3 常见异常
Stream使用之后,必须生成新的流,不能使用原来的stream
所以可以进行链式调用,因为中间操作返回值都是一个新的stream
4.4.3.4 使用方式
package com;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 一个中间操作链,对数据进行处理,一个流可以有0~N个中间操作
*
* 他们每一个都返回新的流,方便下一个进行操作
*
* 但是只能有一个终止操作
*
* fitter : 对元素进行过滤筛选,不符合的就不要了
*
* distinct : 去掉重复元素
*
* skip :跳过多少元素
*
* limit :取最大条数(前几条)
*
* map : 对集合中的元素进行遍历操作
*
* sorted : 排序
*
* @author 人间失格
* @data 2021年11月3日下午6:41:55
*/
public class Stream_01 {
public static void main(String[] args) {
//创建集合,Array.asList将一个数组转换为List集合 asList方法返回的ArrayList数组
List<String>strings = Arrays.asList("a","b","c","a");
//将集合转换成流
Stream<String> stream = strings.stream();
// stream.filter(x->false);
//Stream使用之后,必须生成新的流,不能使用原来的stream
//所以可以进行链式调用,因为中间操作返回值都是一个新的stream
/**
* filter : 对元素进行过滤筛选,不符合的就不要了
*/
//x->!x.equals("a") : 如果返回false,就不要该数据了,如果返回true,就要该数据
//collect(Collectors.toList()) : 终止操作,把stream转换为集合
List<String >value = stream.filter(x->!x.equals("a")).collect(Collectors.toList());
System.out.println(value);
/**
* skip : 跳过 本来是abca跳过两个还剩下ca
*/
//需要重新生成流
stream =strings.stream();
value = stream.skip(2).collect(Collectors.toList());
System.out.println(value);
/**
* map: 对集合中的元素进行遍历并操作
*/
List<Integer>list = Arrays.asList(1000,1200,1100,900,5500);
Stream<Integer>stream2 = list.stream();
//涨薪百分之十
List<Integer>result = stream2.map(x->x+x/10).collect(Collectors.toList());
System.out.println(result);
/**
* distomct :去除重复
*/
stream = strings.stream();
value = stream.distinct().collect(Collectors.toList());
System.out.println(value);
/**
* limit : 前几条
*
* 本来是abcd 前两条是ab
*/
stream = strings.stream();
value = stream.limit(2).collect(Collectors.toList());
System.out.println(value);
/**
* sorted : 排序
*/
// stream2 = list.stream();
// result = stream2.sorted((x,y)->y-x).collect(Collectors.toList());
// System.out.println(result);
//匿名内部类写法 更改为降序排序
stream2 = list.stream();
result = stream2.sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}).collect(Collectors.toList());
System.out.println(result);
}
}
4.4.4 常见的终止操作
4.4.4.1 概述
一旦执行终止操作,中间操作才会真正执行 并且stream也就不能再被使用了
4.4.4.2 常见的终止操作
* forEach : 遍历
*
* collect : 收集器
*
* min,max,count,agerage 计算相关
*
* asyMatch :匹配数据,比如是否包含
4.4.4.3 使用方式
package com;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* 一旦执行终止操作,中间操作才会真正执行,并且stream也就不能再被使用了
*
* forEach : 遍历
*
* collect : 收集器
*
* min,max,count,agerage 计算相关
*
* asyMatch :匹配数据,比如是否包含
*
* @author 人间失格
* @data 2021年11月3日下午7:34:39
*/
public class Stream_02 {
public static void main(String[] args) {
List<String> strings = Arrays.asList("a","b","c","a","c","a");
Stream<String> stream =strings.stream();
//forEach
stream.limit(3).forEach(x->System.out.println(x));
stream = strings.stream();
//获取条数,这种不如直接调用集合中的size方法更加的简单一些
long count =stream.count();
System.out.println(count);
//所以,这样很难体现出count的优势,一般需要结合中间操作执行,优势更大
//例如 统计有多少个a
stream = strings.stream();
//filter : 对元素进行筛选
count = stream.filter(x->x.equals("a")).count();
System.out.println(count);
//获取最大值max
List<Integer> integers =Arrays.asList(1,2,3,4,5);
Stream<Integer>stream2=integers.stream();
Integer i1 = stream2.max((x,y)->x-y).get();
System.out.println(i1);
//匹配数据 anyMatch
stream2 = integers.stream();
boolean flag = stream2.anyMatch(x->x==0);
System.out.println(flag);
//上面 这种使用集合的contains也可以解决,但是也有contains解决不了的问题
System.out.println(integers.contains(5));
//比如好多学生 判断是否有19岁的学生,使用contains的话,Student中就要覆写equals方法了
List<Student>students = new ArrayList<Student>();
students.add(new Student("张一", 18));
students.add(new Student("张二", 19));
students.add(new Student("张三", 17));
students.add(new Student("张四", 18));
students.add(new Student("张五", 19));
students.add(new Student("张六", 20));
students.add(new Student("张七", 21));
Stream<Student>stream3= students.stream();
flag = stream3.anyMatch(a->a.age==19);
System.out.println(flag);
}
}
class Student{
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}