概述
Stream API与InputStream和OutputStream是完全不同的概念,Stream API是对Java中集合操作的增强,可以利用它进行各种过滤、排序、分组、聚合等操作。Stream API配合Lambda表达式可以加大的提高代码可读性和编码效率,Stream API也支持并行操作。
流不是集合,它不关心数据的存放,只关注如何处理数据
Stream API主要用于处理集合操作,不过它的处理方式与传统的方式不同,称为“数据流处理”。流(Stream)类似于关系数据库的查询操作,是一种声明式操作。比如要从数据库中获取所有年龄大于20岁的用户的名称,并按照用户的创建时间进行排序,用一条SQL语句就可以搞定,不过使用Java程序实现就会显得有些繁琐,这时候可以使用流:
List<String> userNames = users.stream()
.filter(user -> user.getAge() > 20)
.sorted(comparing(User::getCreationDate))
.map(User::getUserName)
.collect(toList());
在Java中,集合是一种数据结构,或者说是一种容器,用于存放数据,流不是容器,它不关心数据的存放,只关注如何处理。可以把流当做是Java中的Iterator,不过它比Iterator强大多了。
流使用内部迭代方式处理数据
流与集合另一个区别在于他们的遍历方式,遍历集合通常使用for-each方式,这种方式称为外部迭代,而流使用内部迭代方式,也就是说它帮你把迭代的工作做了,你只需要给出一个函数来告诉它接下来要干什么:
// 外部迭代
List<String> list = Arrays.asList("A", "B", "C", "D");
for (String str : list) {
System.out.println(str);
}
// 内部迭代
list.stream().forEach(System.out::println);
外部迭代更像是作文题,我们不仅要控制元素的迭代方式,还需要定义怎么操作元素;内部迭代更像是填空题,我们只用关注如何操作元素就可以了。
流只能遍历一次
流只能遍历一次,遍历结束后,这个流就被关闭掉了。如果要重新遍历,可以从数据源(集合)中重新获取一个流。如果你对一个流遍历两次,就会抛出java.lang.IllegalStateException异常:
List<String> list = Arrays.asList("A", "B", "C", "D");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 这里会抛出java.lang.IllegalStateException异常,因为流已经被关闭
流通常由三部分构成:
- 数据源:数据源一般用于流的获取,比如本文开头那个过滤用户的例子中users.stream()方法。
- 中间处理:中间处理包括对流中元素的一系列处理,如:过滤(filter()),映射(map()),排序(sorted())。
- 终端处理:终端处理会生成结果,结果可以是任何不是流值,如List;也可以不返回结果,如stream.forEach(System.out::println)就是将结果打印到控制台中,并没有返回。
创建流
由值创建流
使用静态方法Stream.of()创建流,该方法接收一个变长参数:
Stream<Stream> stream = Stream.of("A", "B", "C", "D");
//也可以使用静态方法Stream.empty()创建一个空的流:
Stream<Stream> stream = Stream.empty();