1. Lambda
1.1概述
Lambda表达式是一种没有名字的函数,也可以称为闭包.是java8发布的最重要的新特性.本质上是一段匿名内部类,也可以是一段可以传递的代码.
还有叫箭头函数的
1.2为什么使用Lambda表达式
Lambda表达式就是个匿名内部类的简写方式,使程序更加简单清晰,编程效率也得到了提高
1.3和匿名内部类对比
//2匿名内部类
forEach(arr,new Array(){
public void m1(int i){
System.out.println(i +"-===-");
}
});
//3Lambda表达式
forEach(arr,(i)->System.out.println(i+1+"+++"));
1.4语法结构
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值.
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号.
可选的大括号:如果主体包含了一个语句,就不需要使用大括号.
如果不写{}return 不能写 分括号不能写
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值
如果只有一条语句,并且是返回值语句,就可以不写return 不写{}
如果写上{} 就必须写return 和 ;
如果有多条语句,就必须写{} return 和 ;
1.5案例
1 不需要参数,返回值为5
()->5
2 接收一个参数(数字类型),返回其2倍的值
x->x*2
3 接收两个参数(数字),返回他们的差值
(x,y)->x-y
1.6练习
package day_27text;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class Text02 {
public static void main(String[] args) {
List<Integer>list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
//常规写法
//for(Integer integer :list){
//System.err.println(integer);
//}
//匿名内部类写法
//list.forEach(new Consumer<Integer>(){
//public void accept(Integer t){
//System.out.println(t);
//}
//});
//Lambda写法
list.forEach(x->System.out.println(x));
}
}
package day_27text;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Text03 {
public static void main(String[] args) {
List<Integer>list = new ArrayList<Integer>();
list.add(11);
list.add(2);
list.add(3);
//Cillections.sort(list,new Comparator<Integer>(){
//public int compare(Inreger o1,Integer o2){
//return o2 - o1;
//}
//});
Collections.sort(list,(x,y) -> y - x);
System.out.println(list);
}
}
class B{
public boolean equals(Object obj){
return false;
}
}
2.函数式接口
2.1介绍
应为称为Functional Interface
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口.
核心目标是为了给Lambda表达式的使用提供更好的支持,进一步达到函数式编程的目标,可以通过运用函数式编程极大的提高编程效率.
其可以被隐式转换为Lambda表达式.
2.2特点
函数式接口是仅制定一个抽象方法的接口
可以包含一个或多个静态或默认方法
专用注解即@Functional Interface检查它是否是一个函数式接口,也可不添加该注解
如果有两个或以上抽象方法,就不能当成函数式接口去使用,也不能添加@Functional Interface这个注解
如果只有一个抽象方法,那么@Functional Interface注解加不加都可以当作函数式接口去使用
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.4JDK自带常用的函数式接口
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("构造方法执行了");
}
}
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);
}
}
2.4.3Function<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);
}
}
2.4.4Predicate<T>接口
Predicate<T>接口 断言接口
就是做一些判断,返回值为boolean
有一个boolean test(T)方法,用于校验传入数据是否符合判断条件,返回boolean类型
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.方法引用和构造器调用
2.5对象调用对象
package day_27text03;
import java.util.function.Supplier;
/**
* 对象引用::成员方法名
*
* 要求 : 需要根据调用方法的入参和出参去选择对应的函数式接口才行
*
* @author 学到头秃的张张张
*@Date 2021年11月3日上午3:15:19
*/
public class Text01 {
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());
}
}
2.6类名调用静态
package day_27text03;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
*类名::静态
*
* @author 学到头秃的张张张
*@Date 2021年11月3日上午3:17:09
*/
public class Text02 {
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) {
// TODO Auto-generated method stub
return null;
}
};
fun = x -> Integer.parseInt(x);
fun = Integer::parseInt;
// 前两个是入参,第三个是返回值
BiFunction<Integer, Integer, Integer> bif = Integer::max;
System.out.println(bif.apply(123, 323));
test((B x) -> System.out.println(x));
}
public static void test(A a) {
}
}
interface A {
public void m1(B b);
}
class B {
}
2.7类名调用对象
package day_27text03;
import java.util.function.BiPredicate;
/**
* 类名::成员方法名
*
* @author 学到头秃的张张张
*@Date 2021年11月3日上午3:22:42
*/
public class Text03 {
public static void main(String[] args) {
BiPredicate<String, String> bp = String::equals;
System.out.println(bp.test("abc", "abc"));
}
}
2.8构造方法调用
package day_27text;
import java.util.function.Function;
import java.util.function.Supplier;
public class Text04 {
public static void main(String[] args) {
//无参构造
Supplier<Object> objSi = Object::new;
System.out.println(objSi.get());
//有参构造
Function<String,Integer> function = Integer::new;
System.out.println(function.apply("123"));
//new Integer("123");
}
}
2.9数组引用
package day_27text;
import java.util.function.Function;
public class Text05 {
public static void main(String[] args) {
Function<Integer,Integer[]>function = Integer[]::new;
Integer[] arr =function.apply(10);
for(Integer integer:arr){
System.out.println(integer);
}
}
}
4.Stream API
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 day_27text04;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 生成流
*
* @author 学到头秃的张张张
*@Date 2021年11月3日上午3:37:07
*/
public class Text01 {
public static void main(String[] args) {
// 1 数组转换为流
String[] strings = { "q", "w", "e", "r" };
Stream<String> stream1 = Stream.of(strings);
// 2 通过集合
// Arrays.asList : 把数组转换为集合
List<String> list = Arrays.asList(strings);
stream1 = 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> stream3 = Stream.iterate(1, x->x+2);
stream3.limit(10).forEach(x->System.out.println(x));
// 5 已有类的API
String string = "abc";
IntStream chars = string.chars();
chars.forEach(x->System.out.println(x));
}
}
4.4.3常见中间操作
4.4.3.1概述
* 一个中间操作链,对数据进行处理,一个流可以有0~N个中间操作
*
* 他们每一个都返回新的流,方便下一个进行操作
*
* 但是只能有一个终止操作
4.4.3.2常见中间操作
/**
* filter:对元素进行过滤筛选,不符合的就不要了
*
* distinct:去掉重复元素
*
* skip:跳过多少元素
*
* limit:取最大的条数(前几条)
*
* map:对集合中的元素进行便利操作
*
* sorted:排序
*
* @author 学到头秃的张张张
*@Date 2021年11月4日上午1:39:39
*/
4.4.3.3常见异常
//Stream使用之后,必须生成新的流,不能使用原来的Stream
//所以可以进行链式调用,因为中间操作返回值都是一个新的Stream
//Stream.filter(x->false)
4.4.3.4使用方式
package day_28text;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Text01 {
public static void main(String[] args) {
List<String> strings = Arrays.asList("a","b","c","a");
Stream<String> stream = strings.stream();
//stream.filter(x->false);
//Stream使用之后,必须生成新的流,不能使用原来的stream
//所以可以进行链式调用,因为中间操作返回值都是一个新的stream
//stream.filter(x->false);
/**
* 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(100,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);
/**
* distinct:去除重复
*/
stream = strings.stream();
value=stream.distinct().collect(Collectors.toList());
System.out.println(value);
/**
* limit:前几条
*
* 本来是abca 前两条是[a,b]
*/
stream = strings.stream();
value = stream.limit(2).collect(Collectors.toList());
System.out.println(value);
/**
* sorted : 排序
*/
stream2 = list.stream();
// [900, 1000, 1100, 1200, 5500] 默认升序
// result = stream2.sorted().collect(Collectors.toList());
// 匿名内部类 更改为降序
// [5500, 1200, 1100, 1000, 900]
// result = stream2.sorted(new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// return o2-o1;
// }
// }).collect(Collectors.toList());
// lambda写法
result = stream2.sorted((x, y) -> y - x).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 计算相关
*
* anyMatch 匹配数据,比如是否包含
*
* @author 学到头秃的张张张
*@Date 2021年11月4日上午2:15:29
*/
3.4.4.3使用方式
package day_28text;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Text02 {
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(2).forEach(x->System.out.println(x));
stream = strings.stream();
// 获取条数,这种不如直接调用集合的size方法简单一些
long count = stream.count();
System.out.println(count);
// 所以 这样很难体现出count的优势,一般需要结合中间操作执行,优势更大
// 统计 有多少a
stream = strings.stream();
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==5);
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("赵六", 16));
Stream<Student> stream3 = students.stream();
flag = stream3.anyMatch(u->u.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;
}
}