函数式编程
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
lambda表达式
概述
jdk8中的语法糖函数式编程思想的体现
原则
可推导可省略
格式
(参数列表)->{代码}
例优化多线程启动代码
//匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程run方法执行");
}
}).start();
//lambda写法
new Thread(()->{
System.out.println("run方法执行");
}).start();
例2
public static void printNum(IntPredicate predicate){//参数为java自带的接口
int[] arr = {1,2,3,4,5,6,7,8,9,10};
for (int i : arr) {
if (predicate.test(i)){
System.out.println(i);
}
}
}
内部类写法
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
return value%2==0;
}
});
lambda写法
printNum((a)->a%2==0);//输出数组中为偶数的数
例3类型转换
public static <R> R typeConver(Function<String,R> function){
String str = "1235";
R result = function.apply(str);
return result;
}
内部类写法
Integer integer = typeConver(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
lambda写法
Integer integer1 = typeConver((s) -> Integer.valueOf(s));
省略规则
- 参数类型可以省略
- 方法体只有一句是大括号return和唯一一句代码的分号可以省略
- 方法只有一个参数时小括号可以省略
Stream流
概述
java8的stream使用的是函数式编程模式如同名字一样可以对集合或数组进行链状流式的操作可以更方便的让我们对集合或数组操作
常用操作
创建流
单列集合集合对象.stream()
List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();
数组Arrays.stream(数组)
或 Stream.of(数组)
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
Stream<int[]> arr1 = Stream.of(arr);
双列集合
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);
Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
中间操作
filter
对流中元素进行条件过滤符合条件的才能继续留在流中
例如
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
stream
.filter(a->a>3)//大于3的符合条件可以留在流中
.forEach(System.out::println);
map
可以对流中的元素进行计算或转换
authors.stream()
.distinct()
.map(author -> author.getName())//原本存的是author对象全部转换成String类型的name属性值
.forEach(System.out::println);
distinct
去除流中的重复元素依赖Object的equals方法来判断需要重写equals方法
直接调用没有参数
sorted
可以对流中元素进行排序
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge()- o1.getAge())//o1代表当前
.forEach(System.out::println);
如果调用空参的sorted方法需要流中元素实现Comparable接口
有参的sorted方法即把排序方法搬到了参数中
limit
设置流的最大长度超出的部分将被抛弃
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge()- o1.getAge())
.limit(2)//限制只有两个元素多余的被抛弃
.map(Author::getName)
.forEach(System.out::println);
skip
跳过流中的前n个元素返回剩下的元素
authors.stream()
.distinct()
.sorted()
.skip(1)//跳过第一个
.forEach(System.out::println);
flatMap
map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素
authors.stream()//每个author中有一个book集合
.flatMap(author -> author.getBooks().stream())//获取所有的book对象并整合成新流
.distinct()
.forEach(System.out::println);
authors.stream()
.flatMap(author -> author.getBooks().stream())
.flatMap(book ->Arrays.stream(book.getCategory().split(",")) )//按照,进行切分并转换成流
.distinct()
.forEach(System.out::println);
终结操作
forEach
对流中的元素进行遍历操作我们通过传入的参数去指定遍历到的元素进行什么具体操作
count
用来获取当前流中元素的个数
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();//获取个数
max&min
可以用来获取流中的最值
authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((o1, o2) -> o1-o2);//需要传入比较器
collect
把流转换成集合
list集合
List<String> collect = authors.stream()
.map(author -> author.getName())
.distinct()
.collect(Collectors.toList());//通过工具类
set集合
Set<Book> collect1 = authors.stream()
.flatMap(author -> author.getBooks().stream())
.collect(Collectors.toSet());
map集合
Map<String, List<Book>> collect = authors.stream()
.collect(Collectors.toMap(key -> key.getName(), value -> value.getBooks()));
查找和匹配
anyMatch
可以用来判断是否有任意符合匹配条件的元素结果为boolean类型
boolean b = authors.stream()
.distinct()
.anyMatch(author -> author.getAge() > 29);//判断是否有年龄大于29的数据
allMatch
可以用来判断是否都符合匹配条件结果为Boolean类型都符合为true否则为false
boolean b = authors.stream()
.allMatch(author -> author.getAge() > 18);//判断是否所有数据的年龄都大于18
noneMatch
可以判断流中的元素是否都不符合匹配条件都不符合返回true否则false
boolean b = authors.stream()
.noneMatch(author -> author.getAge() > 100);//判断是否所有数据的年龄都不大于100
findAny
获取流中任意一个元素该方法没有办法保证获取的一定是流中的第一个元素
Optional<Author> any = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();//findAny不可以传参数有筛选条件要先执行
findFirst
获取流中的第一个元素
同上
reduce归并
对流中的数据按照你制定的计算方式计算出一个结果缩减操作
reduce的作用是把stream中的元素给组合起来我们可以传入一个初始值他会按照我们的计算方式依次拿流中的元素和在初始化值的基础上进行计算计算结果再和后面的元素计算
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(0, (result, element) -> result + element);//第一个为初始值后面为要进行的操作
//使用reduce求年龄的最大值
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (o1, o2) -> o2 > o1 ? o2 : o1);
reduce一个参数的重载形式
初始值默认为null计算时如果初始值为null则将第一个值赋给初始值否则执行给定计算
authors.stream()
.map(author -> author.getAge())
.reduce((o1,o2)->o2>o1?o2:o1);
注意
- 没有终结操作流不会执行
- 流是一次性的
- 不会影响原数据
Optional
用于避免空指针异常
使用
创建对象
Optional就好像是包装类可以把具体的数据封装Optional对象内部然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常
一般使用Optional的静态方法ofNullable来把数据封装成一个Optional对象无论传入的参数是否为null都不会出问题
Author author = getAuthor();
Optional<Author> author1 = Optional.ofNullable(author);
如果确定对象不为空则可以使用静态方法of
来封装。此时如果传入null会报空指针
如果需要封装null到Optional中可以使用empty
方法此方法无参数
安全消费值
使用ifPresent()方法如果值为空则不执行任何操作不为空则执行传入的代码
Optional<Author> author1 = Optional.ofNullable(author);
author1.ifPresent(a-> System.out.println(a.getName()));
获取值
可以使用get方法获取当Optional内的值为空时此方法会报异常
安全获取值
orElseGet
获取数据并且设置数据为空时的默认值如果数据不为空就能获取到该数据如果为空则根据你传入的参数来创建对象作为默认值返回
Optional<Author> author1 = Optional.ofNullable(author);
Author author2 = author1.orElseGet(() -> new Author());//此处为null的话会新建一个对象返回
orElseThrow
获取数据如果数据不为空就能获取到数据如果为空则根据你传入的参数来创建异常抛出
Optional<Author> author1 = Optional.ofNullable(author);
Author author3 = author1.orElseThrow(() -> new RuntimeException());//如果为空抛出运行时异常
过滤
可以使用filter()方法对数据进行过滤如果原本有数据但是不符合判断也会变成一个无数据的Optional对象
Optional<Author> author1 = Optional.ofNullable(author);
author1.filter(a -> a.getAge() > 18).ifPresent(a-> System.out.println(a.getName()));
判断
可以使用isPresent进行是否存在数据的判断如果为空返回值为false如果不为空返回值为true。也可以使用ifPresent方法
数据转换
Optional提供了map方法可以让我们对数据进行转换并且转换得到的数据也还是被Optional包装好的
函数式接口
只有一个抽象方法的接口我们称之为函数接口
jdk的函数式接口都加了@FunctionalInterface
注解进行标识
常见函数式接口
Comsumer接口用于消费
Function计算转换接口
Predicate判断接口
Supplier生产型接口
常用默认方法
and
使用predict接口时可能需要进行判断条件的拼接而and方法相当于使用&&来拼接两个判断条件
匿名内部类写法
//打印作家中年龄大于17并且姓名大于1的作家
authors.stream()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge()>17;
}
}.and(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length()>1;
}
})).forEach(System.out::println);
lambda写法
authors.stream()
.filter(((Predicate<Author>) author -> author.getAge() > 17)
.and(author -> author.getName().length()>1))
.forEach(System.out::println);
or
相当于用||拼接两个条件
authors.stream()
.filter(((Predicate<Author>)author -> author.getAge()>17).or(author -> author.getName().length()<2))
.forEach(System.out::println);
negate
表示取反
authors.stream()
.filter(((Predicate<Author>)author->author.getAge()>17).negate())
.forEach(System.out::println);
方法引用
使用lambda时如果方法体中只有一个方法的调用的话可以用方法引用简化代码
格式
类名或对象名::方法名
Stream基本数据类型的优化
map方法转换后对象为包装类如后面要进行运算则会执行自动拆箱和装箱可能影响效率
可以使用mapToxxx方法xxx为数据类型flatMap也有类似方法
并行流
当流中有大量元素时我们可以使用并行流去提高操作效率其实并行流就是把任务分配给多个线程去执行
创建流后调用parallel()
方法即可