一、继续讲解lamdba表达式
函数的引用
lambda表达式是为了简化接口的实现的。 在lambda表达式中, 不应该出现比较复杂的逻辑。 如果在lambda表达式中出现了过于复杂的逻辑, 会对程序的可读性造成非常大的影响。 如果在lambda表达式中需要处理的逻辑比较复杂, 一般情况会单独的写一个方法。 在lambda表达式中直接引用这个方法即可。
或者, 在有些情况下, 我们需要在lambda表达式中实现的逻辑, 在另外一个地方已经写好了。 此时我们就不需要再单独写一遍, 只需要直接引用这个已经存在的方法即可。
函数引用: 引用一个已经存在的方法, 使其替代lambda表达式完成接口的实现。
复习:
package com.qfedu.day16;/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 9:39
* @Version 1.0
*/
/**
* 函数式接口
*/
@FunctionalInterface
interface Flying{
void fly();
}
public class Demo01 {
public static void main(String[] args) {
Flying flying = new Flying() {
@Override
public void fly() {
System.out.println("像风一样自由!!");
}
};
flying.fly();
// 使用的是lambda表达式简化函数式接口的写法
Flying flying1 = () -> System.out.println("像风一样自由!!");
flying1.fly();
}
}
1、静态方法引用
- 语法:
- 类::静态方法
- 注意事项:
- 在引用的方法后面, 不要添加小括号。
- 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。 ```java package com.qfedu.day16;
/**
- @Author laoyan
- @Description TODO
- @Date 2022/3/22 9:47
- @Version 1.0 *
静态方法的引用 */ @FunctionalInterface // 函数式接口,里面有一个未实现的方法 interface SingleReturnMutipleParameter{ int test(int a,int b); } public class Demo02 {
public static void main(String[] args) {
// 没有问题,但是前提条件是方法中的实现代码不多
SingleReturnMutipleParameter singleReturnMutipleParameter = (a,b) -> a+b;
// 如果实现的代码非常的多,我可以编写一个类,类中使用静态方法,编写完成后,直接调用即可
SingleReturnMutipleParameter singleReturnMutipleParameter2 = Computer::jisuan;
int num = singleReturnMutipleParameter2.test(10, 20);
System.out.println(num);
} }
/**
- 这个类,跟接口没有任何关系,没有实现接口
- 类中的方法更不是接口中方法的实现
只要这个方法,参数个数以及参数类型以及返回值类型跟接口中的方法一致,就可以使用。 */ class Computer{
// 是静态的 调用的时候 类名::方法名即可 public static int jisuan(int a,int b){ return a + b; } }
<a name="nJPLy"></a>
### 实战:
```java
package com.qfedu.day16;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 10:03
* @Version 1.0
*/
@FunctionalInterface
interface Converter{
//将字符串转换成整数
Integer convert(String value);
}
public class Demo03 {
public static void main(String[] args) {
Converter converter = (value) -> Integer.valueOf(value);
// 有没有一个类,类中有个静态方法,可以将字符串转换为Integer --> Integer valueOf 方法
Converter converter2 = Integer::valueOf;
System.out.println(converter2.convert("123"));
}
}
2、非静态方法引用
- 语法:
- 对象::非静态方法
- 注意事项:
- 在引用的方法后面, 不要添加小括号。
- 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。 ```java @FunctionalInterface // 函数式接口,里面有一个未实现的方法 interface SingleReturnMutipleParameter{ int test(int a,int b); }
class Computer{
// 是静态的 调用的时候 类名::方法名即可
public static int jisuan(int a,int b){
return a + b;
}
// 是一个普通方法 一个普通类中的普通方法
public int jia(int a,int b){
return a + b;
}
}
main 方中调用: Computer computer = new Computer(); SingleReturnMutipleParameter singleReturnMutipleParameter3 = computer::jia; System.out.println(singleReturnMutipleParameter3.test(20, 30));
// 以上对象只使用了一次,所以我们可以使用匿名对象
SingleReturnMutipleParameter singleReturnMutipleParameter4 = new Computer()::jia;
System.out.println(singleReturnMutipleParameter4.test(20, 30));
<a name="ObFgx"></a>
### 3、构造方法引用
- 语法
- 类名::new
- 注意事项
- 可以通过接口中的方法的参数, 区分引用不同的构造方法。
```java
package com.qfedu.day16;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 10:18
* @Version 1.0
*/
class Person{
private int age;
private String name;
public Person() {
System.out.println("无参数的构造方法已经调用");
}
public Person( String name,int age) {
System.out.println("有参数的构造方法已经调用");
this.age = age;
this.name = name;
}
}
interface NoCanshu{
// 如果某一个函数式接口中定义的方法, 仅仅是为了得到一个类的对象。 此时我们就可以使用构造方法的引用, 简化这个方法的实现。
Person getPerson();
}
interface YouCanshu{
// 如果某一个函数式接口中定义的方法, 仅仅是为了得到一个类的对象。 此时我们就可以使用构造方法的引用, 简化这个方法的实现。
Person getPerson(String name,int age);
}
public class Demo04 {
public static void main(String[] args) {
// 调用无参数的构造方法
NoCanshu noCanshu = Person::new;
System.out.println(noCanshu);
Person person1 = noCanshu.getPerson();
System.out.println(person1);
// 调用有参数的构造方法
YouCanshu youCanshu = Person::new;
Person person = youCanshu.getPerson("张三",19);
System.out.println(person);
}
}
4、对象方法的特殊引用(了解一下)
如果在使用lambda表达式,实现某些接口的时候。 lambda表达式中包含了某一个对象, 此时方法体中, 直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。 其他的参数, 可以作为调用方法的参数。 此时, 可以对这种实现进行简化。
interface SetField {
void set(Person person,String name);
}
SetField setField = (person,name) -> {
person.setName(name);
};
// 以上接口完全满足对象方法的特殊引用
SetField setField2 = Person::setName;
setField2.set(new Person(),"张三");
package com.qfedu.day16;
interface ShowTest {
void test(Person person);
}
interface SetField {
void set(Person person,String name);
}
interface GetField {
String get(Person person);
}
public class Demo05 {
public static void main(String[] args) {
// 使用lambda表达式是这样写
SetField setField = (person,name) -> {
person.setName(name);
};
// 通过::
// 类名:: 方法名一般用于静态方法,目前不是
SetField setField2 = Person::setName;
Person p = new Person("张三", 10);
setField2.set(p,"李四");
System.out.println(p.getName());
GetField getField = Person::getName;
System.out.println(getField.get(new Person("张三", 10)));
ShowTest showTest = (ren) -> {
System.out.println(ren.getName()+","+ren.getAge());
};
ShowTest showTest1 = Person::show;
showTest1.test(new Person("张三", 10));
}
}
二、继续说Stream流—集合流
集合流,使用类Stream来表示,是Java8的新特性之一。使用集合流可以对集合的操作进行功能增强,流并不是集合的元素,也不是一种数据结构,不负责数据的存储操作。更像是迭代器,可以实现对集合中的元素进行单向不循环的遍历。
更像是一种对数组或者集合的一种快速的遍历手段!!!
1、如何获取流
package com.qfedu.day16_02;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 11:25
* @Version 1.0
*/
public class Demo01 {
/**
* 获取流的方式 3种
* @param args
*/
public static void main(String[] args) {
// 快速创建一个带数据的集合
List<Integer> list =new ArrayList<>();
Collections.addAll(list,10,20,35,19,8,22,98,78);
// 通过集合对象获取流对象
Stream<Integer> stream = list.stream();// 串行的流
Stream<Integer> parallelStream = list.parallelStream();// 并行的流
// 通过数组的类Arrays 获取流
int[] arr = {10,20,35,19,8,22,98,78};
IntStream intStream = Arrays.stream(arr);
}
}
2、最终操作
forEach —> 可以循环打印流中的数据
collect —> 可以将流转换为List、Set集合
最终操作,除了这个操作得到的不再是流对象Stream,我们没有办法继续流式的操作下去之外。还有一个很重要的因素: 最终操作会关闭这个流对象本身,我们对一个已经关闭的流进行元素的处理,会有异常!
因此,我们接下来的最终操作的学习过程中切记,千万不要对一个已经关闭的流进行元素的处理。
最终操作都有哪些方法:
// collect 将流中的数据转换为集合
package com.qfedu.day16_02;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 11:39
* @Version 1.0
*/
public class Demo02 {
// 展示Stream流都有哪些最终的操作
public static void main(String[] args) {
List<Integer> list =new ArrayList<>();
Collections.addAll(list,10,20,35,19,8,22,98,78);
// 通过集合对象获取流对象
Stream<Integer> stream = list.stream();// 串行的流
// 获取流中的数据变为集合
//List<Integer> list2 = stream.collect(Collectors.toList());
//Set<Integer> set = stream.collect(Collectors.toSet());
/*Map<Integer, Integer> map = stream.collect(Collectors.toMap(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer integer) {
// key
return integer;
}
}, new Function<Integer, Integer>() {
@Override
public Integer apply(Integer integer) {
// value
return integer + 100;
}
}));
System.out.println(map);*/
/*Map<Integer, Integer> map2 = stream.collect(Collectors.toMap(a -> a, a -> a+100));
System.out.println(map2);*/
//stream.forEach(a -> System.out.println(a));
stream.forEach(System.out::println);
}
}
package com.qfedu.day16_02;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 11:39
* @Version 1.0
*/
public class Demo03 {
/**
* 最终操作:
* collect
* max
* min
* forEach
* count
* reduce
*
* match
* allMath -- 全部元素都满足,才会返回true
* anyMath -- 全部元素部分满足,就会返回true
* noneMath-- 全部元素都不满足,才会返回true
* find
* findFirst --> 都是返回第一个元素
* findAny
*/
// 展示Stream流都有哪些最终的操作
public static void main(String[] args) {
List<Integer> list =new ArrayList<>();
Collections.addAll(list,10,20,35,19,8,22,98,78);
// 通过集合对象获取流对象
Stream<Integer> stream = list.stream();// 串行的流
// reduce 合并的意思,我给定一个流中的两个元素,你看着办
// 得到是一个Optional对象,想要获取里面的最终的值,需要调用get方法
/*Optional<Integer> optional = stream.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer a, Integer b) {
return a + b;
}
});
System.out.println(optional.get());*/
// stream.reduce((a,b) -> a + b).get();
// 获取流中元素的个数
// System.out.println(stream.count());
/*Optional<Integer> max = stream.max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});*/
/*Optional<Integer> max = stream.max((o1,o2) -> o1 - o2 ) ;
System.out.println(max.get());*/
/*Integer min = stream.min((o1, o2) -> o1 - o2).get();
System.out.println(min);*/
// 流中的元素全部满足条件才会返回true
/*boolean match = stream.allMatch((a) -> {
if (a > 50) {
return true;
}
return false;
});
System.out.println(match);*/
/*boolean match1 = stream.anyMatch(a -> {
if (a > 50) {
return true;
}
return false;
});
System.out.println(match1);*/
// 流中的元素必须 全不 满足条件才会返回true
/*boolean match2 = stream.noneMatch(a -> {
if (a > 50) {
return true;
}
return false;
});
System.out.println(match2);*/
//System.out.println(stream.findFirst().get());
//System.out.println(stream.findAny().get());
// 都是返回第一个元素,在串行的流中是一样的,但是在并行的流中不一样。
Stream<Integer> parallelStream = list.parallelStream();
//System.out.println(parallelStream.findFirst().get());
System.out.println(parallelStream.findAny().get());// 在并行的流中,findAny() 打印的数据不固定
}
}
3、中间操作
每个中间操作的返回值都是Stream流对象。
package com.qfedu.day16_02;
import com.qfedu.day16.Person;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 14:40
* @Version 1.0
*/
public class Demo04 {
/**
* Stream的中间操作
* filter 过滤数据
* sorted 排序,默认是数字类型的排序,如果不是数字类型,需要指定比较器
* map 将里面的每一个元素,进行一个操作再返回回去
* distinct 去重
* limit 保留多少个元素
* skip 元素跳过多少个开始算
* flatMap 压扁我们的多维数组变一维
* mapToInt 将需要统计的字段放入进去,就可以求平均值,最大最小值
* @param args
*/
public static void main(String[] args) {
List<Integer> list =new ArrayList<>();
Collections.addAll(list,10,20,20,35,19,8,22,98,78);
// 通过集合对象获取流对象
Stream<Integer> stream = list.stream();// 串行的流
stream.filter(a -> a%2 == 0 ? true:false )
.sorted()//.map(a -> a + 100)
.distinct()
// 8 10 20 22 78 98
.skip(1) // 跳过前面的多少个元素
.limit(3)
.forEach(System.out::println);
String[] arr = {"Hello","World"};
Stream<String> stream1 = Arrays.stream(arr);
/**
* Hello --> [Hell]
* World --> [W, rld]
* flatMap -- 给我一个数组,我给它压扁
* [[1,2,3,4],[5,6,7,8]] --> flatMap --> [1,2,3,4,5,6,7,8]
*/
stream1.map( s -> s.split("o")).flatMap( _arr-> Arrays.stream(_arr)).forEach(System.out::println);
List<Person> pList = new ArrayList<>();
Collections.addAll(pList,
new Person("zhangsan",19),
new Person("lisi",22),
new Person("wangwu",36),
new Person("zhaoliu",78),
new Person("laoyan",19)
);
Stream<Person> stream2 = pList.stream();
// 求 平均年龄
/**
* 就是将元素中的那个属性变为int ,然后求其他的值
*/
/*IntStream intStream = stream2.mapToInt(new ToIntFunction<Person>() {
@Override
public int applyAsInt(Person p) {
return p.getAge();
}
});*/
IntStream intStream = stream2.mapToInt(p -> p.getAge());
// 将我们想要统计的字段放入到流中,然后通过调用方法就可以统计了
//System.out.println(intStream.average().getAsDouble());
System.out.println(intStream.max().getAsInt());
//System.out.println(intStream.min().getAsInt());
}
}
4、综合案例
package com.qfedu.day16_02;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/22 15:21
* @Version 1.0
*/
class Student {
private String name;
private int score;
public String getName() {
return name;
}
public int getScore() {
return score;
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return String.format("姓名: %s, 成绩: %d", name, score);
}
}
public class Demo05 {
/**
* 需求: 一个集合中存储了了若干个Student对象, 要求查询出以下结果:
* 1. 所有及格的学生信息
* 2. 所有及格的学生姓名
* 3. 所有学生的平均成绩
* 4. 班级的前3名(按照成绩)
* 5. 班级的3-10名(按照成绩)
* 6. 所有不不及格的学生平均成绩
* 7. 将及格的学生, 按照成绩降序输出所有信息
* 8. 班级学生的总分
*/
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
Collections.addAll(list,
new Student("xiaoming", 89),
new Student("xiaobai", 98),
new Student("xiaohei", 78),
new Student("xiaolv", 86),
new Student("xiaowang", 59),
new Student("xiaoxiao", 100)
);
Stream<Student> stream = list.stream();
/**
* 1. 所有及格的学生信息
* * 2. 所有及格的学生姓名
* * 3. 所有学生的平均成绩
* * 4. 班级的前3名(按照成绩)
* * 5. 班级的3-10名(按照成绩)
* * 6. 所有不不及格的学生平均成绩
* * 7. 将及格的学生, 按照成绩降序输出所有信息
* * 8. 班级学生的总分
*/
/*stream.filter(p -> p.getScore()>60)
.map(p -> p.getName())
.forEach(System.out::println);*/
//System.out.println(stream.mapToInt(p -> p.getScore()).average().getAsDouble());
//stream.sorted((o1,o2) -> o2.getScore() - o1.getScore()).limit(3).forEach(System.out::println);
//stream.sorted((o1,o2) -> o2.getScore() - o1.getScore()).skip(2).limit(7).forEach(System.out::println);
//System.out.println(stream.filter(p -> p.getScore() < 60).mapToInt(p -> p.getScore()).average().getAsDouble());
System.out.println(stream.mapToInt(p -> p.getScore()).sum());
}
}
三、作业
1、作业
1. 已知一个文本内容如下:
name:xiaoming,age:10岁,gender:male,height:172cm,weight:70kg
age:20岁,height:177cm,name:xiaobai,weight:80kg,gender:male
gender:female,height:176cm,weight:66kg,name:xiaolv,age:21岁
每一行是数据为一个人的信息,但是顺序是不固定的。
1.1、在桌面上创建一个GBK文件,将内容写到这个文件中。(可以使用IDEA创建GBK文件)
1.2、将桌面创建的文件用代码复制到项目下的 file\\source 路径下。
1.3、读取这个文本中的数据,将每一行的数据封装到一个Person对象中,并存入一个List集合
1.4、删除集合中所有的未成年的数据
1.5、计算所有人的平均年龄、最大年龄、最小年龄
1.6、将集合中的数据按照年龄进行降序排序,若年龄相同,按照身高降序排序
1.7、查询集合中所有的年龄在[20, 25]范围内,体重在[60, 80]范围内的数据,按照身高降序排列,截取第4名到第8名。【使用Stream流】
1.8、将集合中的数据序列化到本地
1.9、将本地序列化的文件反序列化,得到集合,并遍历输出里面的内容