您的位置:首页 > 编程语言

助你写出更加有效、简洁、紧凑的代码-Stream

2018-04-01 15:20 531 查看
JAVA8中引入的Stream与 I/O中的InputStream和OutputStream是两个不同的概念。这里的Stream其实是函数式编程里Monad的概念。(在函数式编程中,monad是一个表示计算(步骤序列)的结构。一个带有monad结构的类型或该类型的嵌套函数定义了其链式操作的意义。)

Stream中的操作可以分为两大类:

中间操作与结束操作

1. 中间操作:中间操作返回Stream,这样我们就可以在不使用分号的情况下串联多个中间操作

中间操作又可以分为无状态(Stateless)操作与有状态(Stateful)操作,前者是指元素的处理不受之前元素的影响;后者是指该操作只有拿到所有元素之后才能继续下去。

2. 结束操作:终端操作返回void或者一个非Stream结果值

结束操作又可以分为短路与非短路操作,这个应该很好理解,前者是指遇到某些符合条件的元素就可以得到最终结果;而后者是指必须处理所有元素才能得到最终结果。



不同类型的Stream

可以从各种数据源创建Stream,特别是collections,List 和 Set, 支持新方法 stream() 和parallelStream(),以创建顺序或并行Stream。如:

Arrays.asList("a1", "a2", "a3")
.stream()
.findFirst()
.ifPresent(System.out::println);


使用 Stream.of() 从一堆对象引用中创建一个Stream,如:

Stream.of("a1", "a2", "a3")
.findFirst()
.ifPresent(System.out::println);


除了常规的对象Stream,Java 8有特殊类型的Stream,用于处理基本数据类型int,long和double。分别是IntStream、LongStream和DoubleStream。

IntStreams可以使用IntStream.range()来代替常规的for循环,如:

IntStream.range(1, 4)
.forEach(System.out::println);


将普通Stream转为原生类型的Steam:mapToInt()、mapToLong()和mapToDouble

处理顺序

中间操作的一个重要特征是惰性。以下例子中,终端操作是缺失的,在执行此代码片段时,不会向控制台输出任何内容。这是因为中间操作只在出现终端操作时执行:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
});


通过终端操作forEach来扩展上面的例子:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.forEach(s -> System.out.println("forEach: " + s));


控制台输出:

filter:  d2
forEach: d2
filter:  a2
forEach: a2
filter:  b1
forEach: b1
filter:  b3
forEach: b3
filter:  c
forEach: c


输出顺序:每个元素都沿着链垂直移动。第一个字符串“d2”先filter然后foreach,然后第二个字符串“a2”才被处理。

这种方式可以减少在每个元素上执行的实际操作数,如下例所示:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.anyMatch(ss->{
System.out.println("anyMatch: "+ss);
return ss.equals("a2");
});


控制台输出:

filter: d2
anyMatch: d2
filter: a2
anyMatch: a2


当predicate应用于给定的输入元素时,anyMatch将立即返回true。这对于第二个被传递的“a2”来说是正确的。由于stream链的垂直执行,在这种情况下,filter只会执行两次。因此,filter将尽可能少地被调用。

处理顺序很重要

下一个示例包括两个中间操作 map 和 filter 以及终端操作forEach。我们再一次查看这些操作是如何执行的:

Stream.of("d2", "a2", "b1", "b3", "c")
.map(s -> {
System.out.println("map: " + s);
return s.toUpperCase();
})
.filter(s -> {
System.out.println("filter: " + s);
return s.startsWith("A");
})
.forEach(s -> System.out.println("forEach: " + s));


控制台输出:

map: d2
filter: D2
map: a2
filter: A2
forEach: A2
map: b1
filter: B1
map: b3
filter: B3
map: c
filter: C


如输出结果所示,底层集合中的每个字符串都被调用了5次map和filter,而forEach只调用一次。

改变操作的顺序,将filter移到链的开头,可以大大减少实际执行次数:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return s.startsWith("a");
})
.map(s -> {
System.out.println("map: " + s);
return s.toUpperCase();
})
.forEach(s -> System.out.println("forEach: " + s));


控制台输出:

filter: d2
filter: a2
map: a2
forEach: A2
filter: b1
filter: b3
filter: c


如输出结果所示,map只被调用一次,因此操作管道在大量元素输入时执行得更快。

Stream复用

Stream在你调用任何终端操作后,就会关闭:

Stream<String> stringStream = Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
});
stringStream.anyMatch(ss->{
System.out.println("anyMatch: "+ss);
return ss.equals("a2");
});
stringStream.forEach(ss->{
System.out.println("forEach: "+ss);
});


在同一条Stream上的调用anyMatch之后调用noneMatch导致以下异常:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at com.java8.stream.StreamTest.t8(StreamTest.java:113)
at com.java8.stream.StreamTest.main(StreamTest.java:26)


可通过为要执行的每一个终端操作创建一个新的Stream链的方式解决上述问题。

Supplier<Stream<String>> streamSupplier = ()->Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
});
streamSupplier.get().anyMatch(ss->{
System.out.println("anyMatch: "+ss);
return ss.equals("a2");
});
streamSupplier.get().forEach(ss->{
System.out.println("forEach: "+ss);
});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java8 stream