Java 8中一个重大的改变是对核心类库的改进,新增的特性旨在帮助我们写出更加优秀的代码。对核心类库的改进主要包括集合类的 API 和新引入的流 (Stream)。流使程序员得以站在更高的抽象层次上对集合进行操作。
从外部迭代到内部迭代
外部迭代
我们在使用集合类时,最常用的就是利用for循环或者迭代器对集合进行操作,比如如下的代码:
private static List<Long> extractLongListValue(String productTags) {
List<Long> list = new ArrayList<>();
if (StringUtils.isBlank(productTags)) {
return list;
}
String[] parts = productTags.split(",");
for (String part : parts) {
if(StringUtils.isBlank(part)){
continue;
}
list.add(Long.valueOf(part));
}
return list;
}
复制代码
for循环其实是迭代器的语法糖,调用 iterator 方法,产生一个新的 Iterator 对象,进而控制整 个迭代过程,这就是外部迭代。迭代过程通过显式调用 Iterator 对象的 hasNext 和 next 方法完成迭代。
外部迭代的问题主要是:
- 很难抽象一些通用操作。
- 本质上是串行操作。
内部迭代
Java 8中的stream()方法为我们提供了使用内部迭代的机会。steam()不是返回一个控制迭代的 Iterator 对象,而是返回内部迭代中的相应接口:Stream。内部迭代将更多控制权交给了集合类,底层是通过访问者模式来实现。
改造后的代码:
private static List<Long> extractLongListValue(String productTags) {
return Arrays.stream(productTags.split(Constant.COMMA)).filter(s -> StringUtils.isNotBlank(s)).map(Long :: valueOf).collect(
Collectors.toList());
}
复制代码
直观的可以看到代码更加清晰简洁,业务逻辑清晰。如果有多个for循环嵌套,那么利用stream的优势将更加明显。
常用流操作
流上的操作主要分两种:
- 惰性求值方法,只描述stream,不产生新集合,返回值为stream。类似建造者模式中的Builder。
- 及早求值方法,最终会从stream产生值,具有终止操作,返回值为另一个值或空。
map
private static List<Long> extractLongListValue(String productTags) {
return Arrays.stream(productTags.split(Constant.COMMA)).filter(s -> StringUtils.isNotBlank(s)).map(Long :: valueOf).collect(
Collectors.toList());
}
复制代码
比如这段代码中,是利用Long
的valueOf
函数将类型为String流转换为类型为Long流。
filter
filter()
功能是保留Stream中的一些元素,根据条件过滤掉其他元素。filter()
需要传入一个Predicate函数接口的Lambda,Predicate接口传入一个对象,返回一个boolean。例如上述例子中的s -> StringUtils.isNotBlank(s)
。
collect
collect方法属于及早求值方法,传入一个收集器,将流生成一个复杂的结构。常用的收集器有:
(1)转换成其他集合
在上述例子中,我们就用了toList()
收集器产生了一个新的List。在这里我们不需要指定类型,Stream类库会自动为我们挑选合适的类型。有时候我们需要产生一些特殊类型的集合,比如TreeSet
,此时我们可以用toCollection()
并传入TreeSet
的构造函数:
stream.collect(Collectors.toCollection(TreeSet:new));
复制代码
(2)数据分组
groupingBy()
的功能和SQL中的group by类似,根据值对流进行分组,最后返回一个map。
Map<Long, List<SkuDTO>> skuMap = skuList.stream().collect(groupingBy(SkuDTO::getSkuId));复制代码