函数式编程-Stream流
作用:
处理大数量集合:并行流,多线程处理
代码可读性高
消灭嵌套地狱
1、Lambda表达式
- 要求:接口,并且只有一个抽象方法
- 区别:函数式编程,不同于面对对象。不关注类名、方法名;关注参-数、方法体
- 格式:删除参数、方法体外部分,在中间加 ->
lambda表达式再简化:
- 参数类型可以省略
- 方法体只有一句代码时大括号return和唯一一句代码的分号可以省略
- 方法只有一个参数时小括号可以省略
2、Stream流使用:
2.1、创建流
获取流对象
- 单列集合:;
- 数组:(arr); (arr);
- 双列集合:转换为单列集合,在转换为流 ().stream(); 或keySet();等转换
2.2、中间操作
中间操作,返回值都是stream流
- filter 过滤,符合条件保存在集合中
- map(new Function<Ob1,Ob2>) 计算、数据类型的转换;将现在流中类型Ob1转换为Ob2,返回Ob2
- distinct 去重,依赖equals方法,需要重写equals方法。
- sorted 排序:
- 空参的sorted方法需要流中元素实现了Comparable接口
- 有参的sorted方法,在内部类里定义比较
- limit 设置流的最大长度,超出部分抛弃掉
- skip 跳过流中前n元素,留下之后的
- flatMap
把一个对象转换为多个对象成为流中的元素。一个对象中的某个属性是list集合,将这些对象的属性都加入流中处理。
2.3、终结操作
流操作最后必须有终结操作,才会执行前面的其他操作。
- foreach 将元素遍历进行操作
- count 获取流中元素的个数
- max/min 获取流中的最值,返回Optional
- collect(()) 将流对象转换为集合
2.4、查找与匹配
-
anyMatch 判断是否有匹配的,有任意一个匹配的,结果为true
-
allMatch 判断是否都符合
-
noneMatch 判断都不符合
-
findAny 获取任意一个元素
-
findFirst 获取流中的第一个元素
-
reduce 归并:对流中数据按照你指定的计算方式计算出一个结果。(缩减操作)
//reduce两个参数内部原理
//遍历流中元素,与初始值进行运算,返回结果
T result = identity;
for(T element : this stream){
result = accumulator.apply(result,element)
}
return result;
例子:求所有作者年龄的和
List<Author> authors = getAuthors();
Integer sum = authors.stream()
.distinct() //去重
.map(author -> author.getAge()) //类型转换,作者对象转换为年龄Integer
.reduce(0,(result,element) -> result + element);//初始值为0,每个年龄与初始值相加
System.out.println(sum); //输出年龄的和
2.5、注意事项
- 惰性求值:没有终结操作,中间操作不会执行
- 流是一次性的:一个流对象经过一个终结操作后,这个流不能再被使用
- 不会影响原数据:流中的处理,正常情况下不会影响原来集合中的元素
3、Optional
作用:判断对象是否为空,避免空指针异常
3.1、获取optional对象
//获取optional对象
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
//获取optional对象,mybatis 3.5后支持直接返回optional对象
//开发中从数据库查数据,可直接返回Optional<Author>对象,由框架封装
public static Optional<Author> getAuthorOptional(){
Author author = new Author(12, "xy");
return Optional.ofNullable(author);
}
3.2、安全地消费值
authorOptional.ifPresent(new Consumer<Author>() {
@Override
public void accept(Author author1) {
System.out.println(author1.getName());
}
});
3.3、安全地获取值
3.3.1、orElseGet
//设置默认值。有值则返回该值;如果为null没有值,则返回new Author()默认值
Author author = authorOptional.orElseGet(new Supplier<Author>() {
@Override
public Author get() {
return new Author();
}
});
orElseGet源码
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
3.3.2、orElseThrow
try {
//有值则返回值,没有值则抛出自定义的异常,可用于spring中统一异常管理
Author author2 = authorOptional1.orElseThrow(new Supplier<Throwable>() {
@Override
public Throwable get() {
return new RuntimeException("数据为null");
}
});
} catch (Throwable e) {
throw new RuntimeException(e);
}
3.4、过滤
//返回年龄大于18岁的optional对象,不大于18岁被过滤掉
Optional<Author> author3 = authorOptional1.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 18;
}
});
//过滤后消费
authorOptional1.filter(author22 -> author22.getAge() > 18)
.ifPresent(author23 -> System.out.println(author23));
3.5、判断
isPresent判断是否存在数据。为空则返回false;如果不为空则返回true
if (authorOptional1.isPresent()){
System.out.println(authorOptional1.get().getName());
}
3.6、类型转换
将作家集合转换为书的集合
Optional<Object> optionalBooks =
authorOptional1.map(new Function<Author, Object>() {
@Override
public Object apply(Author author) {
return author.getBook();
}
});
4、函数式接口
//接口中只有一个抽象方法(可以有default修饰的默认方法),该接口为函数式接口。
//注解@FunctionalInterface标注,一定是函数式接口
@FunctionalInterface
public interface InterfaceA {
void test();
}
4.1、常见函数式接口
- Consumer 消费接口
根据参数列表(T t)和返回值类型(void),可对传入参数进行消费。
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
- Function 计算转换接口
根据参数列表(T t)和返回值类型(R),可将参数T转换为R进行返回。
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
- Predicate 判断接口
根据转入条件进行判断,返回判断结果
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
- Supplier 生产型接口
没有参数,需要返回对象。我们可以在方法中创建对象,把创建好的对象返回
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
4.2、常见默认方法
多用于自定义的函数式方法。
- Predicate 中的 and() 方法。短路与 &&。
authorOptional1.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 18; //可替换为 () > 18 && ().length() < 5
}
}.and(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length() < 5;
}
})).ifPresent(new Consumer<Author>() {
@Override
public void accept(Author author) {
System.out.println(author);
}
});
- Predicate 中的 or() 方法。
- Predicate 中的 negate() 方法,取反。
5、方法引用
在使用lambda时,如果方法体中只有一个方法的调用的话(包括构造方法),可以用方法引用进一步简化代码。
5.1、基本格式
类名或对象名 ::方法名
5.2 使用(待续)
快捷键 alt + 回车 ,replace lambda with method reference。
6、高级用法
6.1、取消自动装拆箱
//将age自动装箱为Integer,每有一个操作都会装拆箱,数据量大时浪费时间。
authors.stream()
.map(new Function<Author, Integer>() {
@Override
public Integer apply(Author author) {
return author.getAge() + 10;
}
})
//用mapToInt返回int类型的值。
authors.stream()
.mapToInt(new ToIntFunction<Author>() {
@Override
public int applyAsInt(Author author) {
return author.getAge();
}
})
6.2、并行流
有大数据量时,使用多线程进行处理。
authors.stream().parallel()
.peek(new Consumer<Author>() { //peek()中间操作,可用于调试
@Override
public void accept(Author author) {
System.out.println(author + Thread.currentThread().getName()); //查看线程名
}
})