前言
Java 8 的stream相关api大大方便了对集合类型的各种操作,其使用的函数式思想使得api易用,但是抽象层次加深,对理解造成一定的影响。我们从最简单的操作入手,逐步理解使用其设计思维。
一、简单的stream操作拆解?
此处不列举stream的具体实现,只看其api提供的入口以及结果。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
List<String> back = collect.stream().map(Object::toString).collect(Collectors.toList());
Stream接口的方法map,接收一个Function,返回Stream。
对比直接使用循环的操作,抽象层次明显加深。这种抽象使得我们思考时也需要按照它的设计来思考。直接使用循环,我们想的是我需要将集合里的数据转换为另一个数据,至于怎么转换、还有没有其他操作,可以后面再添加。而使用stream流式操作,我们的思考方式需要变成:我需要这个集合的一种映射,映射关系帮助我完成。
在不同的思维下,实现方式也不同,stream定义了映射关系的抽象函数,并根据抽象函数实现了映射流程,我们只需要根据它定义的抽象函数来实现我们的映射关系。这就是函数式编程的思想。
根据这种思想,不难理解一些简单的stream操作了。
二、稍难理解的stream操作
1.flatMap
示例
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
List<String> strList = Stream.of("1,2,3", "9,6,4").collect(Collectors.toList());
List<String> strNumber = strList.stream()
.map(str -> str.split(","))
.flatMap(Arrays::stream)
.collect(Collectors.toList());
//分隔线
public static <T> Stream<T> stream(T[] array) {
return stream(array, 0, array.length);
}
可以看到,flatmap与map基本类似,区别在于映射函数的返回值定义不同。map定义的返回函数很简单,没有限制,但flatmap定义的返回函数限定为Stream的子类。
基于函数式思想,map是集合元素的一一对应映射,一个元素对应一个元素;而flatmap是一对多映射,即一个元素,分裂为流中的多个元素(使用流包裹)。于是很容易读懂它的操作了。
上面这个流式操作,map将字符串映射为字符串数组,flatmap将字符串数组映射为字符串,而arrays.stream 方法其签名符合flatmap要求的Function,于是这里可以直接使用。
加深理解
为了理解flatmap的使用,示例如下:
List<Integer> num = Stream.of(1, 5, 10).collect(Collectors.toList());
List<Integer> multiNum = num
.stream()
.flatMap(Demo::streamForFlatMapTest)
.collect(Collectors.toList());
System.out.println(multiNum);
//分隔线
public static Stream<Integer> streamForFlatMapTest(Integer in) {
return Stream.<Integer>builder()
.add(in - 1)
.add(in)
.add(in + 1)
.build();
}
可以看到,flatmap与map的不同,仅仅在于返回值,map返回单实例,flatmap返回流。
总结
提示:仅代表个人理解
以上就是从Java的stream操作理解函数式编程的内容。希望通过对flatmap的理解,可以加深我们对函数式思想的认知。