一、Lambda表达式
1、函数式接口
如果说,一个接口中,要求实现类必须实现的抽象方法,有且只有一个!这样的接口就是函数式接口*注:接口里的方法有default修饰(有方法体),子类可以重写也可以不重写
代码语言:javascript复制//非函数式接口,Object类中有默认的toString方法,可以不重写
interface Test{
String toString();
}
2、@FunctionalInterface注解
用在接口之前,判断这个接口是否是一个函数式接口。如果是函数式接口,没有任何问题。如果不是函数式接口,则会报错。
代码语言:javascript复制@FunctionalInterface
interface FunctionalInterfaceTest{
void test();
}
3、基础语法
代码语言:javascript复制(参数类型 参数,参数类型 参数)->{
方法体
};
4、语法进阶
(1)参数的类型可以不写,在接口方法中已经定义过。
(2)如果需要省略参数的类型,要保证:要省略,每一个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没省略
代码语言:javascript复制//多个参数、无返回值的方法实现
NoneReturnMutipleParameter lambda1=(a,b)->{
System.out.println("多个参数、无返回值方法的实现:参数a是" a ", 参数b是" b);
}
(3)如果参数列表中的参数数量有且只有一个,此时,参数列表的小括号是可以省略不写的,省略小括号的同时必须省略参数的类型
代码语言:javascript复制//有参、无返回值的方法实现
NoneReturnSingleParameter lambda2=a->{
System.out.println("一个参数、无返回值方法的实现:参数是 " a);
}
(4)当一个方法体中的逻辑,有且只有一句的情况下,大括号可以省略
代码语言:javascript复制//有参、无返回值的方法实现
NoneReturnSingleParameter lambda2=a->System.out.println("一个参数、无返回值方法的实现:参数是 " a);
(5)如果一个方法中唯一的一条语句是一个返回语句,此时在省略大括号的同时,也必须省略return
代码语言:javascript复制SingleReturnMultipleParameter lambda3=(a,b)->a b;
5、函数引用:引用一个已经存在的方法,使其替代lambda表达式完成接口的实现
(1)静态方法的引用
- 语法 类::静态方法
- 注意事项:引用的这个方法,参数(数量、类型)和返回值,必须要跟接口中定义的一致
public class Syntax1 {
public static interface Calculate{
int calculate(int a,int b);
}
//静态方法的引用
public static void main(String[] args){
//实现一个多个参数的、一个返回值的接口
//对一个静态方法的引用
Calculate lambda1=Syntax1::calculate;
System.out.println(lambda1.calculate(,));
}
private static int calculate(int x,int y){
if(x>y){
return x-y;
}else if(x<y){
return y-x;
}
return x y;
}
}
(2)非静态方法的引用
- 语法 对象::非静态方法
- 引用的这个方法,参数(数量、类型)和返回值,必须要跟接口中定义的一致
public class Syntax1 {
public static interface Calculate{
int calculate(int a,int b);
}
//静态方法的引用
public static void main(String[] args){
//实现一个多个参数的、一个返回值的接口
//对一个静态方法的引用
Calculate lambda1=Syntax1::calculate;
System.out.println(lambda1.calculate(,));
//对一个非静态方法的引用
Calculate lambda2=new Syntax1()::calculate2;
System.out.println(lambda2.calculate(,));
}
private int calculate2(int a,int b){
if(a!=b){
return a-b;
}else{
return a b;
}
}
private static int calculate(int x,int y){
if(x>y){
return x-y;
}else if(x<y){
return y-x;
}
return x y;
}
}
(3)构造方法的引用
- 语法 类名::new
- 可以通过接口中的方法的参数,区分引用不同的构造方法
public class Lambda2 {
public static class Person{
String name;
int age;
public Person(){
System.out.println("Person类的无参构造方法执行了");
}
public Person(String name){
this.name=name;
System.out.println("Person类的有参数构造方法执行了");
}
public Person(String name,int age){
this.name=name;
this.age=age;
System.out.println("Person类的两个参数构造方法执行了");
}
}
private interface GetPersonWithNoneParameter{
Person get();
}
private interface GetPersonWithSingleParameter{
Person get(String name);
}
private interface GetPersonWithMultipleParameter{
Person get(String name,int age );
}
public static void main(String[] args){
GetPersonWithNoneParameter getPerson=Person::new;
getPerson.get();
GetPersonWithSingleParameter getPerson1=Person::new;
getPerson1.get("张三");
GetPersonWithMultipleParameter getPerson2=Person::new;
getPerson2.get("张三",);
}
}
(4)对象的特殊用法
- 如果lambda表达式中包含了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。其他的参数可以作为调用方法的参数。此时可以简化为 类::方法,方法不加括号
public class Lambda3 {
public static class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface MyInterface{
//String get(Person person);
void set(Person person,String name);
}
public static void main(String[] args){
Person xiaoming=new Person();
xiaoming.setName("xiaoming");
// MyInterface lambda1=x->x.getName();
// MyInterface lambda2=Person::getName;
// System.out.println(lambda2.get(xiaoming));
MyInterface lambda3=(x,n)->x.setName(n);
MyInterface lambda4=Person::setName;
lambda4.set(xiaoming,"123");
System.out.println(xiaoming.getName());
}
}
(5)lambda表达式注意事项
代码语言:javascript复制public class Lambda5 {
private static int y=;
public static void main(String[] args){
//1.定义一个局部变量
int x=;
//2.使用lambda表达式实现接口
LambdaTest lambda=()->{
System.out.println("x=" x);
System.out.println("y=" y);
};
//3.修改变量x的值(此处是局部变量,不能更改值,不注销)
//x=20;
//4.修改变量y的值
y=;
}
}
二、集合的流式编程
- 简介:把数据源中的数据读到Stream流里面,对Stream流里面的数据进行操作(删除、过滤、映射等),每次操作结果也是一个流对象,可以对这个流再进行其他的操作,最后将Stream流里的数据放到集合或者数组里。
- 流式编程的步骤
(1)获取数据源,将数据源中的数据读取到流中
(2)对流中的数据进行各种各样的处理
(3)对流中的数据进行整合处理
其中(2)称为中间操作,(3)称为最终操作。
1、数据源的获取
(1)数据源的简介
*注意:将数据读取到流中进行处理的时候,与数据源中的数据没有关系。也就是说,中间操作对流的数据进行处理、过滤、映射、排序等,此时是不会影响数据源中的数据的
(2)数据源的获取
代码语言:javascript复制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;
/**
* 集合流式编程中的数据源的读取
* 读取数据源中的数据,得到一个流对象
*/
public class DataSource {
public static void main(String[] args){
collectionDataSource();
arrayDataSource2();
}
/**
* 将list作为数据源,读取list中的数据到一个流中
*/
public static void collectionDataSource(){
//1.实例化一个集合
List<Integer> list=new ArrayList<>();
//2.填充元素
Collections.addAll(list,,,,,,,,,,);
//3.读取集合中的数据,将其读取到流中
Stream<Integer> stream=list.stream();
//4.输出stream
System.out.println(stream);
}
public static void arrayDataSource(){
//1.实例化一个数组
Integer[] array=new Integer[]{,,,,,,,,,};
//2.读取数组中的数据到流中,得到一个流对象
Stream<Integer> stream= Arrays.stream(array);
//3.输出stream
System.out.println(stream);
}
public static void arrayDataSource2(){
//1.实例化一个数组
int[] array=new int[]{,,,,,,,,,};
//2.读取数组中的数据到流中,得到一个流对象
IntStream stream= Arrays.stream(array);
//3.输出stream
System.out.println(stream);
}
}
2、最终操作
(1)将流中的数据收集到一起,对这些数据进行一些处理,最常见的处理,就是将流中的数据存入一个集合。
*注意:最终操作,之所以叫最终操作,是因为,在最终操作执行结束后,会关闭这个流,流中的所有数据都会销毁。如果使用一个已经关闭了的流,会出现异常。
(2)collect:将流中的元素放到集合里
代码语言:javascript复制import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 最终操作
*/
public class FinalOperation {
public static void main(String[] args){
collectUsage();
}
/**
* 最终操作:collect
*/
public static void collectUsage(){
//1.获取数据源
Stream<Integer> dataSource=getDataSource();
//2.流中的数据的聚合
//List<Integer> list=dataSource.collect(Collectors.toList());
Map<String,Integer> list=dataSource.collect(Collectors.toMap(i->i.toString(), i->i));
System.out.println(list);
}
/**
* 数据源的获取,从一个容器中获取数据源中的数据
* @return读取数据源中的数据,得到一个Stream对象
*/
public static Stream<Integer> getDataSource(){
//1.准备一个容器
List<Integer> dataSource=new ArrayList<>();
//2.向数据源中添加数据
Collections.addAll(dataSource,,,,,,,,,,);
//3.读取数据源中的数据,得到Stream对象并返回
return dataSource.stream();
}
}
(3)reduce:将流中的数据按照一定的规则聚合起来
代码语言:javascript复制//将流的元素,逐一带入到这个方法中,进行运算
//最终的运算结果,得到的其实是一个Optional类型,需要使用get()获取到里面的数据
int result=list.stream().reduce((e1,e2)->e1 e2).get();
System.out.println(result);
(4)count:统计流中元素的数量
代码语言:javascript复制long result=list.stream().count();
System.out.println(result);
(5)forEach:迭代、遍历流中的数据
代码语言:javascript复制list.stream().forEach(System.out::println)
(6)max&&min
代码语言:javascript复制list.stream().min(Integer::compareTo).get();
(7)Matching
- allMatch:只有当流中所有元素,都匹配指定的规则,才会返回true
- anyMatch:只要流中有任意的数据,满足指定的规则,都会返回true
- noneMatch:只有当流中的所有的元素,都不满足指定的规则,才会返回true
//判断流中是否所有的元素都大于50
boolean result=list.stream().allMatch(ele->ele > );
//判断流中是否有大于50的数据
boolean result=list.stream().anyMatch(ele->ele > );
//判断流中是否都是偶数
boolean result=list.stream().noneMatch(ele->ele%!=);
(8)find
- findFirst:从流中获取一个元素(获取的是开头的元素)
- findAny:从流中获取一个元素(一般情况下,是获取的开头的元素) 这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下,这两个返回的结果可能不一样。
Integer integer=list.stream().findFirst().get();
Integer integer=list.stream().findAny().get();
(9)IntStream的使用
代码语言:javascript复制int[] array=new int[]{,,,,,,,,,};
IntStream stream=Arrays.stream(array);
System.out.println(stream.max().getAsInt());
System.out.println(stream.min().getAsInt());
System.out.println(stream.sum());
System.out.println(stream.count());
System.out.println(stream.average().getAsDouble());
//获取到一个对流中的数据的分析结果
IntSummaryStatistics intSummaryStatistics=stream.summaryStatistics();
System.out.println("max = " intSummaryStatistics.getMax());
System.out.println("min = " intSummaryStatistics.getMin());
System.out.println("sum = " intSummaryStatistics.getSum());
System.out.println("average = " intSummaryStatistics.getAverage());
System.out.println("count = " intSummaryStatistics.getCount());
3、中间操作:将数据从数据源中读取到流中,中间操作就是对流中的数据进行各种各样的操作、处理。中间操作可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作,直到最终操作。
(1)filter:条件过滤,仅保留流中满足指定条件的数据,其他不满足的数据都会被删除
(2)distinct:去除流中重复的数据,需要重写对象的hashCode和equals方法
(3)limit&&skip:limit表示截取流中指定数量的数据,skip表示跳过指定数量的数据,截取剩余的部分
(4)map:对流中的数据进行映射,用新的数据替换旧的数据
代码语言:javascript复制list.stream().map(ele->ele ".txt").forEach(System.out::println);
IntSummaryStatistics intSummaryStatistics=dataSource.mapToInt(Student::getScore).summaryStatistics();
4、Collectors工具类
Collectors
类提供了大量方法用于指示如何收集元素。
比如 Collectors.toList()
方法可以将流中的元素收集起来,并转换为列表
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " filtered);
比如 Collectors.joining()
方法可以将流中的元素收集起来,并使用指定的字符串拼接符拼接成一个字符串。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " mergedString);