前言:流提供了一种在比集合更高的概念级别上指定计算的数据视图。通过流,可以将操作的调度留给具体实现去解决。
1、用流操作代替迭代:
通常我们使用迭代遍历集合的元素并执行某项操作。例如,计算一个字符串集合中长单词的个数,使用迭代的方式:
List list = Arrays.asList("ArrayList", "Arrays", "String", "class", "main");
int count = 0;
for (String s : list) {
if (s.length() > 5) {
count++;
}
}
System.out.print(count);
使用流的方式:
List list = Arrays.asList("ArrayList", "Arrays", "String", "class", "main");
long count = list.stream().filter(s -> s.length() > 5).count();
System.out.print(count);
1.1、流遵循了“做什么而非怎么做”的原则。只需描述需要“做什么”,而不用确切地指定“怎么做”。
1.2、流与集合的差异:
(1)流不存储其元素。元素可能存储在底层的集合中,或按需生成。
(2)流的操作不会修改其数据源。例如filter方法不会从流中移除元素,而是会生成一个新的流,其中不包含与条件不符合元素。
(3)流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如上例,只想在前n个元素中查找,那么filter方法将在查到到第n个元素后停止操作。因此,使用甚至可以操作无限流。
1.3、操作流的典型流程:
(1)创建一个流;
(2)指定将初始流转换为其他流的中间操作,可能包含多个步骤;
(3)应用终止操作,产生结果。这个操作会强制执行之前的惰性操作。至此,这个流无法再使用。
2、流的创建:
2.1、上述例子已经看到了Collection接口的stream方法将集合转换为一个流的操作。下面还有几种方式:
(1)Stream.of(T... values)方法:具有可变长参数,可以创建具有任意数量引元的流。
Stream stream = Stream.of("Stream", "of", "流", "123");
(2)Stream.generate(Supplier extends T> s)方法:创建无限流的静态方法。
Stream generate = Stream.generate(() -> "generate");//创建一个常量值的流
Stream generate1 = Stream.generate(Math::random);//产生无限序列
(3)Arrays.stream()方法:有多个不同参数类型和个数的方法,可以根据需要选择。
Stream stream = Arrays.stream(new String[]{"Arrays", "stream" });
(4)Stream.empty()方法:创建一个不包含任何元素的流。
Stream empty = Stream.empty();
2.2、filter、map和flatMap方法:
流的转换会产生一个新的流,它的元素派生自另一个流中的元素。
(1)filter通过筛选出与条件匹配的元素转换为一个新的流;
(2)map方法接收一个作用于流中元素的函数用来转换流中的值,并生成一个新的流;
(3)flatMap方法与map方法一样用来转换流中的值,同时将生成的新流中的所有元素摊开连接到一起(默认通过“,”分隔)。
2.3、抽取子流和连接流:
(1)Stream.limit(n)方法:产生一个新流,在n个元素之后结束。对于裁剪无限流的尺寸很有用;
(2)Stream.skip(n)方法:丢弃前n个元素,产生一个不包括被丢弃的前n个元素的新流。
(3)Stream.contact(Stream a,Stream b)方法:连接流a和流b,产生一个新的流,其中的元素是包括流a的元素后面加上流b的元素。
2.4、其他的流转换:
(1)Stream.distinct()方法:产生一个流,包含当前流中所有不同的元素;
(2)Stream.sorted(Comparator comparator)方法:产生一个流,其中的元素是当前流按照提供的排序方式排序后的顺序。
(3)Stream.peed(Consumer action)方法:产生一个流,与当前流中的元素相同,在获取每个元素时,会将其传递给action。