问题:为什么要用Optional这个类?
说是解决空指针异常,大致是这个意思,这个问题先留着,先看用法
用法:
Optional name = Optional.of(“”) 创建对象传入的参数不能为null,否则就会报空指针异常
疑问:不是解决空指针异常吗,怎么自己又搞出个空指针?
Optional name = Optional.ofNullable(null) 创建对象传入的值可以为null
创建完之后我们就可以调用对象的相关方法了:
isPresent():
如果值存在就返回true, 不存在就返回false
疑问:这个和 if (name = null) 有啥区别吗?这不是麻烦了吗?
get():
如果里面有值,就返回,否则就抛出NoSuchElementException异常
疑问:是没有空指针异常了,但是又出现个这个异常,就是改下名,该出错还是会出错
ifPresent()
如果里面有值则调用里面的consumer,也就是lambda表达式,否则不做处理
解惑:哦,原来如此,以往我们要处理一个值,如果为空的话就报空指针异常了,但是如果把值放在Optional里,然后要处理的时候,直接调用这个方法,即使为空,就不做任何处理了,但是只能放lambda表达式啊,如果我逻辑复杂,并且需要修改值,怎么办,还有就是不报异常,我上哪找错误
orElse():
如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。
疑惑:这个三元表达式就可以完成,我为什么要用这个方法呢?
orElseGet:
orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。
疑惑:不管是用函数引用或者lambda表达式,不都是生成默认值吗,没啥区别啊
orElseThrow:
和上面的一样,但是Supplier接口实现的是指定的异常,就是如果为空,就抛出指定的异常
疑惑:还是抛异常,感觉把空指针异常转换成了各种异常
map() 如果有值,则调用里面的lambda表达式进行更改,否则就返回空的Optional
想法:这个map可以对其进行更改,并且返回Optional,也就是我可以无限串联的执行逻辑
flatMap():
他和map的区别就是,map返回值类型可以是任何类型,但是最后会用Optional封装,
也就是说如果返回值类型是Optional,那么封装完之后就是Optional
但是flatMap必须返回的是Optional类型,没什么封不封装的
filter()
就是传入一个lambda表达式进行过滤,如果满足条件则返回这个Optional,否则返回空Optional
我了解的方法就是这些了
思考
前面的get() 和 isPresent() 方法没啥用,一个没值就抛异常,一个判断是否有值,这跟之前的if(value = null)一样
相比来说: orElse
orElseGet
orElseThrow
虽然三元表达式也可以完成,但是这个起码是个方法,勉强可以接受吧
然后ifPresent 就是存在对他做点什么,但是无法改变传入的值
而map,filter我认为还是很有用处的,
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}
并且map返回的还是Optional,也就可以级联调用
看下这个:
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
User user = .....
if(user != null) {
String name = user.getUsername();
if(name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}
总结:
就是可能我们会对为空的字段做操作,这样就会报空指针异常,但是我觉得应该都会考虑空指针异常,那么这个Optional类只是让我们把那些if val == null else 这样的语句省去了,这样代码就变得简洁了,但是我总感觉其实这个类没太大必要,因为如果简单的逻辑完全可以用三元运算符代替,如果有那种if else 嵌套多的,并且还是判断为空的可以考虑,其他的我觉得没必要换,但是如果觉得高大上,可以用
二, Stream
什么是Stream? 文件流?字符流?字节流?我暂且认为就是对集合的再一次封装。
问题:为什么要有Stream?
遍历集合需要使用for,foreach,迭代器,这些难道我们都不需要写了吗?
先看用法:
分三部分
首先得需要拿一个集合对象来获取自己的流
然后一顿中间操作,就是你要对集合进行什么操作,你就写什么,但是这时候其实还没有执行这些方法
最后是终止操作,执行这个才将所有的操作一起执行
首先要获取流,获取流的方法有很多种
Collection接口就提供了两个获取流的方法,一个顺序流,一个是并行流
所以你可以像这样获取流:
List list = new ArrayList<>();
Stream stream = list.stream(); // 获取一个顺序流
其他的方法就不介绍了
那么拿到流之后,就可以一顿中间操作了
比如你要筛选就可以用filter方法,接受lambda表达式
比如去重那么就可以用distinct方法,根据hashCode和equals
比如你不想操作所有元素,那么就用limit(n)方法,给定一个最大值
skip(n)返回一个扔掉了前n个元素的流,与limit互补
上面的方法大概就是过滤和筛选吧,但真正要执行一些业务逻辑的话应该是下面的方法:
比如你想好了怎么处理函数,那么就自己写一个函数,然后传到map里,你写的函数的参数类型和个数要和遍历的集合元素一致,然后返回Stream这类型就好了,如果逻辑简单的话,或者现有函数可以完成,直接传入就可以了
还有一种flatmap方法,它和map的区别我看别人写的代码
它是可以把多个流连接成一个流:
-
List words = new ArrayList();
-
words.add(“your”);
-
words.add(“name”);
-
public static Stream characterStream(String s){
-
List<Character> result = new ArrayList<>();
-
for (char c : s.toCharArray())
-
result.add(c);
-
return result.stream();
-
}
-
Stream<Stream> result = words.map(w -> characterStream(w));
-
Stream letters = words.flatMap(w -> characterStream(w));
如果使用的是map方法,返回的是[ …[‘y’, ‘o’, ‘u’, ‘r’], [‘n’, ‘a’, ‘m’, ‘e’]]
如果使用的是flatMap方法,返回的是[‘y’, ‘o’, ‘u’, ‘r’, ‘n’, ‘a’, ‘m’, ‘e’]
最后还有个排序的方法:
sorted()
不传参数就是默认排序,传参数就是自定义排序
最后就是终止操作了:
执行这个操作,才真的执行上述一系列的中间操作:
比如你想要知道流中经历了层层筛选最后剩下多少数目,就可以用count方法
求最大值max,最小值min, 返回第一个元素findFirst, 或者做一些匹配,比如剩下的元素还是不是你想要的,则可以用allMatch, anyMath noneMatch方法
但是上述方法你可能都不想要,把stream转化为集合类型也是终止操作
方法是collect
例如如果想把流转化成list类型可以用:
collect(Collectors.toList())
总结:
因为不可以map里不可以改变外部的局部变量,所以有些for不可以被stream取代
性能问题:https://blog.csdn.net/caoxiaohong1005/article/details/79091946 你可以去这里面具体看一下
- 对于简单操作推荐使用外部迭代手动实现,
- 对于复杂操作,推荐使用Stream API,
- 在多核情况下,推荐使用并行Stream API来发挥多核优势,
4.单核情况下不建议使用并行Stream API。