Day19
课程内容
1、Lambda表达式
2、函数式接口
3、内置型函数式接口
4、Stream的使用
一、Lambda表达式
1、本质:就是对接口匿名内部类的一种简写格式
2、前提:接口,必须是函数式接口(只有一个抽象方法)
3、表示就是一个接口的实现类对象
格式详解
1、格式:
(参数列表) —>{方法体};
2、说明:
(1)(参数列表)
表示要实现的接口中,抽象方法的参数
(2)->
箭头运算符,lambda运算符,就是分隔前后两部分内容的
(3)方法体:(lambda体)
对抽象方法的具体实现
3、语法格式1:没有参数,没有返回值
()—> {方法体};
简写格式,如果lambda中只有一条语句,可是省略大括号
语法格式2:有一个参数,没有返回值
(int x)->{方法体};
如果只有一个参数,小括号是可以省略的,而且参数的类型的声明也是可以省略的
x-> {方法体};
语法格式3:有多个参数,有返回值
(x,y)->{方法体};
简写格式:如果只有return这一条语句,就可以省略大括号和return
eg:(x,y)->{return x + y;} 等价于 (x,y) -> x + y;
public class Demo_1 {
public static void main(String[] args) {
// lambda表达式
// 匿名内部类实现
Inter_3 i3 = new Inter_3() {
@Override
public int test(int x, int y) {
int sum = x + y;
return sum;
}
};
int test = i3.test(4, 9);
System.out.println(test);
// lambda表达式实现
/*Inter_3 i33 =(x,y) -> {
int sum = x + y;
return sum;
};
int test2 = i33.test(98, 100);
System.out.println(test2);*/
// 简写格式:如果只有return这一条语句,就可以省略大括号和return
Inter_3 i33 =(x,y) -> x + y;
int test2 = i33.test(98, 100);
System.out.println(test2);
}
private static void test_2() {
// 匿名内部类
Inter_2 i2 = new Inter_2() {
@Override
public void test(int x) {
System.out.println(x);
}
};
i2.test(100);
// lambda表达式实现
/*Inter_2 i22 = (int x) -> {
System.out.println(x + "lambda实现");
};
i22.test(999);*/
// 简写格式:如果只有一个参数,小括号是可以省略的,而且参数的类型的声明也是可以省略的
Inter_2 i22 = m -> System.out.println(m + "lambda实现");
i22.test(999);
}
private static void inter_1() {
// 匿名内部类
Inter_1 i1 = new Inter_1() {
@Override
public void test() {
System.out.println("test方法");
}
};
i1.test();
// lambda表达式实现
/*Inter_1 i2 = () -> {
System.out.println("lambda实现的方法");
};*/
// 语法格式1,简写格式,如果lambda中只有一条语句,可是省略大括号
Inter_1 i2 = () -> System.out.println("lambda实现的方法");
i2.test();
}
}
//接口必须是函数式接口
//没有参数,没有返回值g
interface Inter_1 {
void test();
}
//有一个参数,没有返回值
interface Inter_2 {
void test(int x);
}
//有多个参数,有返回值
interface Inter_3 {
int test(int x, int y);
}
二、函数式接口
1、lambda前提就是函数式接口
2、定义:
在一个接口中,只要一个抽象方法,那么这个接口就是函数式接口
3、检查
@FunctionalInterface
可以使用该注解完成对函数式接口的检查
如果不是函数式接口,编译报错
4、理解:
(1)函数:想表达的是一个方法的内容,由于方法不在任何类中,称为函数
(2)函数式接口:想要表达的是方法,不再是接口
5、使用原因
java语言,不支持将方法的实现逻辑作为一个参数进行传递
解决:只能在方法的外面加上一层接口的声明,将来表面上传递方法所在接口的实现类对象,实际上是想间接的传递方法的实现逻辑。
public class Demo_2 {
public static void main(String[] args) {
// 函数式接口
FunInter_1 f1 = new FunInter_1() {
@Override
public void show() {
System.out.println("show方法");
}
};
FunInter_1 f2 = ()->System.out.println("lambda实现的方法");
test(f2);
}
public static void test(FunInter_1 f1) {//传递方法的实现逻辑
f1.show();
}
}
@FunctionalInterface
interface FunInter_1{//在方法的外面套上了一层接口
void show();
}
三、常用的内置型函数式接口
1、内置型函数型接口
java8中提供了一些常用的内置型函数式接口,在使用类似功能的时候,就不需要额外定义接口,直接使用jdk中提供的即可
2、罗列:
Consumer<T>: 消费型接口
void accept(T t);
Supplier<T>: 供给型接口
T get();
Function<T,R>:函数型接口
R apply(T t);
Predicate<T>: 断言型接口
boolean test(T t);
消费型接口
1、Consumer<T>: 消费型接口
2、void accept(T t)
3、作用
将来需要接受一个数据,并进行处理,处理之后,不需要返回数据,这个时候就可以使用消费型接口
之前只能传递要处理的数据,现在可以传递处理数据的逻辑(方式,方法)
import java.util.function.Consumer;
public class Demo_3 {
// 消费型接口
public static void main(String[] args) {
// test("java"); 只能传递要处理的数据
Consumer<String> con = x -> {//处理的逻辑,截取从2索引开始字符串
String substring = x.substring(2);
System.out.println(substring);
};
Consumer<String> con1 = x -> {//处理的逻辑,
int length = x.length();
System.out.println(length);
};
test(con1, "java");
}
public static void test(Consumer<String> con,String str) {
con.accept(str);
}
public static void test(String str) {
int length = str.length();
System.out.println(length);
}
}
方法引用
1、写一个函数式接口的时候,方法的实现,已经被某个其他的对象实现了,就不需要在lambada体中,再次实现这个方法,可以直接使用其他对象中已经定义好的方法
2、格式:
函数式接口 名称 = 对象名 :: 方法名;
函数式接口 名称 = 类名:: 静态方法名;
3、作用
把已经实现的方法,作为一个数据,作为实现类对象,赋值给函数式接口的引用
可以把这个引用当做方法的返回值,也可以作为方法的参数来进行传递。
4、本质:可以将任意的一个方法,作为函数式接口的一个实现类对象
import java.util.function.Consumer;
public class Demo_4 {
public static void main(String[] args) {
// 方法引用
Test test = new Test();
test.show("长沙是个好地方");
// Consumer<String> con = x -> System.out.println(x);
// con.accept("长沙怎么老下雨");
// accept(T t)一个参数,没有返回值
// Consumer<String> con = test :: show;
Consumer<String> con = System.out :: println;
con.accept("长沙怎么老下雨");
test(test::show);
//
}
public static void test(Consumer<String> con) {
con.accept("hello");
}
}
class Test{
public void show(String str) {//一个参数,没有返回值
System.out.println(str);
}
}
供给型接口
1、Supplier<T>
2、名称:供给型接口
3、抽象方法: T get()
4、作用:
需要定义一个方法,没有参数,但是可以产出一些数据,就可以使用供给型接口
可以传递生产数据的方式
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
public class Demo_5 {
public static void main(String[] args) {
// 供给型接口
// List<Integer> num = getNum();
// 在调用方法的时候,传入生产数据的方式
Supplier<Integer> sup = () -> {// 生产数据的逻辑
Random random = new Random();
return random.nextInt(20) + 1;
};
List<Integer> num = getNum(sup);
System.out.println(num);
}
public static List<Integer> getNum(Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(sup.get());// 这里是不知道要添加什么样的数据
}
return list;
}
public static List<Integer> getNum() {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
return list;
}
}
函数型接口
1、Function<T,R>
2、R apply(T t)
3、作用
将来需要将一个数据进行处理,并返回处理后的结果,就可以使用函数型接口
以前我们传入要处理的数据,现在可以传入处理该数据的逻辑,方法,方式
4、方法:
andThen(Funciton f)先对要处理的数据按照调用者函数型接口对象的中的逻辑进行处理,将处理后的结果作为参数函数型接口f中的参数,进行第二次处理,将结果返回
import java.util.function.Function;
public class Demo_6 {
public static void main(String[] args) {
// 函数型接口
// 处理数据的逻辑
/*Function<String, String> fun = m -> {
String substring = m.substring(2);
return substring;
};
funcitionInter(fun, "java");*/
Function<String, String> fun1 = x -> x.toUpperCase();
Function<String, Integer> fun2 = m -> m.length();
funcitionInter(fun1, fun2, "hello");
}
// andThen方法
public static void funcitionInter(Function<String, String> fun1, Function<String, Integer> fun2, String str) {
// String strsString = fun1.apply(str);// 先将str按照fun1指定的逻辑进行处理
// Integer apply = fun2.apply(strsString);
//
// System.out.println("一次处理后的结果" + strsString);
// System.out.println("二次处理后的结果" + apply);
Integer apply = fun1.andThen(fun2).apply(str);//先对str按照fun1中的逻辑
进行处理,将处理后的结果作为fun2的参数,进行第二次处理,将结果返回
System.out.println(apply);
}
// Function<String, Integer> fun 处理str的逻辑,String str要处理的字符串
public static void funcitionInter(Function<String, String> fun, String str) {
String strsString = fun.apply(str);// 使用fun完成了对str的处理,但是不知道是怎么样去处理str这个数据
System.out.println(strsString);
}
public static int swtichData(String str) {
return str.length();// String 数据传入方法,得到一个int类型数据
}
}
断言型接口
1、Predicate<T> 断言型接口
2、boolean test(T t)
3、作用:
需要定义一个函数,接口一个数据,并对该数据进行判断,判断该数据是否满足指定的条件,返回一个boolean类型的值,就可以是断言型接口
以前只能传入要判断的数据,现在可以传入对数据的判断逻辑
4、方法
and(Predicate<T> pre) 在调用者条件判断之后,再由参数条件进行判断,最后返回同时满足两个条件的结果
or(Predicate<T> pre) 在调用者条件判断之后,再由参数条件进行判断,最后返回满足任意一个条件的结果
negate(Predicate<T> pre) 调用者条件判断取反
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class Demo_7 {
public static void main(String[] args) {
// 断言型接口
List<String> list = new ArrayList<String>();
list.add("张飞");
list.add("张三");
list.add("张伟");
list.add("张益达");
list.add("诸葛亮");
list.add("司马相如");
list.add("上官婉儿");
// List<String> newList = filterList(list);
// System.out.println(newList);
Predicate<String> pre = m -> m.startsWith("张");
Predicate<String> pre1 = m -> m.length() == 4;
List<String> filterList = filterList(list,pre1);//传入判断数据的逻辑
System.out.println(filterList);
// ---------------and-----------------
/* Predicate<String> pre1 = m -> m.startsWith("张");
Predicate<String> pre2 = m -> m.length() == 4;
List<String> filterList = filterList(list,pre1,pre2);
System.out.println(filterList);*/
}
// and or
public static List<String> filterList(List<String> list,Predicate<String> pre1,Predicate<String> pre2){
List<String> newList = new ArrayList<String>();
for (String string : list) {
// if(pre1.test(string) && pre2.test(string)) {
// if(pre1.and(pre2).test(string)) {//和上面实现的效果是等价的
if(pre1.or(pre2).test(string)) {
newList.add(string);
}
}
return newList;
}
// negate
public static List<String> filterList(List<String> list,Predicate<String> pre){
List<String> newList = new ArrayList<String>();
for (String string : list) {
// if(!pre.test(string)) {
if(pre.negate().test(string)) {
newList.add(string);
}
}
return newList;
}
// 对list集合中的数据进行筛选,只要姓张的
public static List<String> filterList(List<String> list) {
List<String> newList = new ArrayList<String>();
for (String string : list) {
if(string.startsWith("张")) {
newList.add(string);
}
}
return newList;
}
}
四、Stream(流)
1、在java8中,提供了一个Stream类型
2、作用:
可以快速完成对数据的过滤(筛选)
Stream类型数据的获取
1、Collection的获取
调用Stream()方法,返回Stream类型的对象
2、Map集合
(1)keySet方法,Set<Key> 再去调用stream()方法
(2)entrySet方法,Set<Key,Value>再去调用stream()方法
(3)values方法,Collection<Value>再去调用stream()方法
3、数组的获取
Stream.of(数组)
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Stream;
public class Demo_8 {
//Stream流的获取
public static void main(String[] args) {
// Collection set,list,ArrayList,LinkedList,HashSet
HashSet<String> hs = new HashSet<>();
Stream<String> s = hs.stream();
// Map
HashMap<String, Integer> hm = new HashMap<String, Integer>();
Set<String> keySet = hm.keySet();
Stream<String> stream = keySet.stream();
Set<Entry<String, Integer>> entrySet = hm.entrySet();
Stream<Entry<String, Integer>> stream2 = entrySet.stream();
Collection<Integer> values = hm.values();
Stream<Integer> ssStream = values.stream();
// 数组,数组获取Strema类型的方式
Integer[] arr = {1,2,3,2,32,32,32,32,3};
Stream<Integer> of = Stream.of(arr);
}
}
Stream中的常用方法
1、大多数方法都是对流中的数据,进行过滤或者筛选的。
2、分类:
终结方法:调用完之后,返回值不再是Stream类型了,无法继续使用Stream中的方法
eg:forEach,count
延迟方法:调用完之后,返回值还是Stream类型了,可以继续使用Stream中的方法
eg:skip,limit,map,filter
3、常用的方法
Stream filter(Predicate<T> pre)
根据pre描述的条件,对流中的数据进行过滤
void forEach(Predicate<T> pre)
将流中的数据,根据con指定的数据处理方式,进行处理
Stream limit(long n);
根据参数n描述的个数,对流中的数据进行截取
Stream skip(long n)
跳过参数n所描述元素的个数,流中剩余的是n之后的内容
long count();
返回流中元素的个数。
Stream map(Function<T, R> fun)
将流中所有T类型的数据,根据fun指定的处理逻辑,转换成R类型的数据
static Stream concat(Stream steam1,Stream stream2)
将流stream1和stream2两个流中的数据合并到一个流中
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo_9 {
public static void main(String[] args) {
// Stream的常用方法
List<String> list = new ArrayList<String>();
list.add("许仙");
list.add("青儿");
list.add("法海");
list.add("白素贞");
list.add("许三多");
list.add("许文强");
// 获取一下该集合的Stream流
Stream<String> stream1 = list.stream();
// filter(Predicate<T> pre)
Predicate<String> pre = x -> x.length() == 2;
Stream<String> stream2 = stream1.filter(pre).filter(x -> x.startsWith("许"));
// forEach(Consumer<T> t) 将流中的数据,根据con指定的数据处理方式,进行处理
// Consumer<String> con = x->System.out.println(x);
// filter.forEach(x->System.out.println(x));
stream2.forEach(System.out::println);//一旦Stream对象调用了终结方法eg:foreach就不能再使用了
// stream2.filter(x->x.length()==4);
// --------limit----------------------
System.out.println("--------limit----------------------");
Stream<String> limitStream1 = list.stream();
limitStream1.limit(3).forEach(System.out::println);
// --------skip----------------------
System.out.println("--------skip----------------------");
Stream<String> limitskip = list.stream();
limitskip.skip(3).forEach(System.out::println);
// --------count----------------------
System.out.println("--------count----------------------");
Stream<String> limitcount = list.stream();
long count = limitcount.skip(3).count();
System.out.println(count);
// --------map----------------------
// map(Function<T, R> fun) 将流中所有T类型的数据,根据fun指定的处理逻辑,转换成R类型的数据
System.out.println("--------map----------------------");
Stream<String> mapStream = list.stream();
Function<String, Person> fun = m -> new Person(m);
mapStream.map(fun).forEach(System.out::println);
// Function<String, Integer> fun = x-> x.length();
// mapStream.map(fun).forEach(System.out::println);;
// --------concat----------------合并两个流对象
System.out.println("--------concat--静态方法--------------------");
// 要求:要合并的两个流对象不能调用终结方法的
Stream<String> conStream1 = list.stream();
Stream<String> filter1 = conStream1.filter( x->x.length() == 2 );
Stream<String> conStream2 = list.stream();
Stream<String> filter2 = conStream2.filter(x->x.startsWith("白"));
// 合并两个流
Stream<String> concat = Stream.concat(filter1, filter2);
concat.forEach(System.out::println);
}
}