Java8 Stream
在看到Java8的新特性之后很想了解下Java8里面的新东西,所以根据Java8出现的新Stream类以及相关操作写一些自己简单的理解,如果哪里写的不对有大神看到还希望帮忙纠正,要是有写的不好的也请帮忙指正,我也希望自己在总结的过程中可以进步,也希望能帮到其他人。
Stream对象提供多个非常有用的方法,这些方法可以分成两类:
中间操作
将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
终端操作
产生的是一个结果或者其它的复合操作;如count或者forEach操作。
map & flatmap
源码:
/**
* Returns a stream consisting of the results of applying the given
* function to the elements of this stream.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param <R> The element type of the new stream
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function to apply to each element
* @return the new stream
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
/**
* Returns a stream consisting of the results of replacing each element of
* this stream with the contents of a mapped stream produced by applying
* the provided mapping function to each element. Each mapped stream is
* {@link java.util.stream.BaseStream#close() closed} after its contents
* have been placed into this stream. (If a mapped stream is {@code null}
* an empty stream is used, instead.)
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @apiNote
* The {@code flatMap()} operation has the effect of applying a one-to-many
* transformation to the elements of the stream, and then flattening the
* resulting elements into a new stream.
*
* <p><b>Examples.</b>
*
* <p>If {@code orders} is a stream of purchase orders, and each purchase
* order contains a collection of line items, then the following produces a
* stream containing all the line items in all the orders:
* <pre>{@code
* orders.flatMap(order -> order.getLineItems().stream())...
* }</pre>
*
* <p>If {@code path} is the path to a file, then the following produces a
* stream of the {@code words} contained in that file:
* <pre>{@code
* Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
* Stream<String> words = lines.flatMap(line -> Stream.of(line.split(" +")));
* }</pre>
* The {@code mapper} function passed to {@code flatMap} splits a line,
* using a simple regular expression, into an array of words, and then
* creates a stream of words from that array.
*
* @param <R> The element type of the new stream
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function to apply to each element which produces a stream
* of new values
* @return the new stream
*/
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
根据源码可以看到map和flatmap返回的都是一个stream对象,所以都是一个中间操作,但是map和flatMap有些不同,我们可以理解为map会比flatmap多了一层封装,具体理解可以参考下面示例
示例代码1:
public static void test(){
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.map(n -> Stream.of(n.split(""))).forEach(System.out::println);
Stream<String> s1 = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s1.flatMap(n -> Stream.of(n.split(""))).forEach(System.out::println);
}
输出结果:
java.util.stream.ReferencePipeline$Head@34c45dca
java.util.stream.ReferencePipeline$Head@52cc8049
java.util.stream.ReferencePipeline$Head@5b6f7412
java.util.stream.ReferencePipeline$Head@27973e9b
java.util.stream.ReferencePipeline$Head@312b1dae
t
e
s
t
t
1
t
2
t
e
e
e
e
e
a
a
a
a
示例代码2:
package com.troy.java.stream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* 1.flatMap和Map都返回一个Stream
* 2.flapMap返回的Stream是将每一个元素都当作一个Stream返回,因此flatMap的结果可以直接通过遍历拿到我们想要的单个对象的值,也就是说flatMap返回的结果是一个list
* 同时flatmap传入的值在处理晚后返回的结果也必须是一个list;
* 3.Map返回的Stream是将m处理的结果做为一个Stream.ReferencePipeline的集合返回,所以当我们尝试按照String来获取map出来的对象是拿不到的;
* 4.通过对比结果我们可以这样来理解,map最后的结果是一个stream但是这个stream包裹了所有map处理的对象,当我们要拿对象的时候要多处理一层;
* flatmap返回的直接就是处理的结果的集合,没有外面Stream包裹这一层
*/
public class Java8Map {
public static void main(String[] args){
List list = new ArrayList();
list.add("hello world");list.add("Java 8");list.add("Spring Springboot SpringCloud");
System.out.println("=============flatMap============");
list.stream().flatMap(m->{
System.out.println("m.toString == " + m.toString());
return Stream.of(m.toString().split(" "));
}).forEach(f->System.out.println(f));
System.out.println("================================");
System.out.println();
System.out.println("=============Map============");
list.stream().map(m->{
System.out.println("m.toString == " + m.toString());
return Stream.of(m.toString().split(" "));
}).forEach(f->{
System.out.println(f.getClass());
System.out.println(f.toString());
Iterator<String> iterable = ((Stream) f).iterator();
while(iterable.hasNext()){
System.out.println(iterable.next());
}
});
System.out.println("================================");
}
}
输出结果:
=============flatMap============
m.toString == hello world
hello
world
m.toString == Java 8
Java
8
m.toString == Spring Springboot SpringCloud
Spring
Springboot
SpringCloud
================================
=============Map============
m.toString == hello world
class java.util.stream.ReferencePipeline$Head
java.util.stream.ReferencePipeline$Head@b4c966a
hello
world
m.toString == Java 8
class java.util.stream.ReferencePipeline$Head
java.util.stream.ReferencePipeline$Head@2f4d3709
Java
8
m.toString == Spring Springboot SpringCloud
class java.util.stream.ReferencePipeline$Head
java.util.stream.ReferencePipeline$Head@4e50df2e
Spring
Springboot
SpringCloud
================================
通过两个示例可以看到同样的操作执行结果之后flatMap可以输出我们期待的遍历结果,但是map返回的是一个stream.ReferencePipeline,因此需要我们多做一步解析才能拿到map的value
示例代码3:
public static void test(){
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.map(n -> Stream.of(n.split(" "))).forEach(System.out::println);
Stream<String> s2 = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s2.map(n -> n.split(" ")).forEach(System.out::println);
Stream<String> s1 = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s1.flatMap(n -> Stream.of(n.split(" "))).forEach(System.out::println);
Stream.of(1,2,3).map(n->n+3).forEach(System.out::println);
Stream.of(1,2,3).flatMap(n->n+3).forEach(System.out::println);
Stream.of(1,2,3).flatMap(n->Stream.of(n+3)).forEach(System.out::println);
String[] args = {"hello","ups","java"};
Stream.of(args).map(m->m.concat("~m")).forEach(f->System.out.println(f));
Stream.of(args).flatMap(m->m.concat("~m")).forEach(f->System.out.println(f));
}
我们可以看到有两行代码编译报错,这是因为flatMap里面需要的是一个mapper stream function,如果我们直接传一个function过来程序编译会报错,因此这两行编译不通过,我们也可以看到编译通过flatMap语句里面的mapper function都是一个stream包裹的操作。这一点我们也可以通过map和flatMap源码里面的注释可以看到
map注释:
Returns a stream consisting of the results of applying the given function to the elements of this stream.
flatMap注释:
Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is {@link java.util.stream.BaseStream#close() closed} after its contents have been placed into this stream. (If a mapped stream is {@code null} an empty stream is used, instead.)
This is an intermediate operation.
@apiNote
The {@code flatMap()} operation has the effect of applying a one-to-many transformation to the elements of the stream, and then flattening the resulting elements into a new stream.
Stream.of(1,2,3).flatMap(n->n+3).forEach(System.out::println);
Stream.of(args).flatMap(m->m.concat("~m")).forEach(f->System.out.println(f));
注释掉这两行之后运行结果可以看到
java.util.stream.ReferencePipeline$Head@4e50df2e
java.util.stream.ReferencePipeline$Head@1d81eb93
java.util.stream.ReferencePipeline$Head@7291c18f
java.util.stream.ReferencePipeline$Head@34a245ab
java.util.stream.ReferencePipeline$Head@7cc355be
[Ljava.lang.String;@52cc8049
[Ljava.lang.String;@5b6f7412
[Ljava.lang.String;@27973e9b
[Ljava.lang.String;@312b1dae
[Ljava.lang.String;@7530d0a
test
t1
t2
teeeee
aaaa
4
5
6
4
5
6
hello~m
ups~m
java~m
最后总结:
通过几个例子可以看到map和flatMap的区别:
- 执行map时的匿名函数可以直接是一个方法体,不需要stream包裹,但是flatMap需要多用Stream包裹一层;
- map和flatMap函数结束的时候返回的结果是不一样的,map操作会比flatMap操作要多包裹一层,这点需要我们在写代码的时候注意去做合适的处理;
reduce
reduce的相关操作参考Java8-Lambda & InterfaceAPI入门里面对于BiFunction和BinaryOperator部分的介绍和示例,这里就不再过多介绍了
其他相关操作未完待续。。。