作者:曹伟,叩丁狼教育高级讲师。原创文章,转载请注明出处。
强大的Stream API
什么是Stream
Java8中有两大最为重要的特性。
1)Lambda 表达式,已经学习过了;
2)Stream API (java.util.stream.*包下)
说到Stream便容易想到I/O Stream,而实际上我们这里讲的Stream它是Java8中对数据处理的一种抽象描述;
我们可以把它理解为数据的管道,我们可以通过这条管道提供给我们的API很方便的对里面的数据进行复杂的操作!比如查找、过滤和映射(类似于使用SQL);
更厉害的是可以使用Stream API 来并行执行操作;
简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式,解决了已有集合类库操作上的弊端。
●注意:
1.请暂时忘记对传统IO流的固有印象!
2.Stream接口继承关系
图中4种stream接口继承自BaseStream,其中IntStream, LongStream, DoubleStream对应三种基本类型(int, long, double,注意不是包装类型),Stream对应所有剩余类型的stream视图。为不同数据类型设置不同stream接口,可以提高性能,并针对不同数据类型提供不同方法实现。那什么不把IntStream等设计成Stream的子接口?毕竟这接口中的方法名大部分是一样的。答案是这些方法的名字虽然相同,但是返回类型不同,如果设计成父子接口关系,这些方法将不能共存,因为Java不允许只有返回类型不同的方法重载。
传统集合操作vs Stream API操作
传统方式遍历集合
几乎所有的集合(如Collection 接口或Map 接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。例如:
List list = new ArrayList<>();
list.add("马云");
list.add("马化腾");
list.add("李彦宏");
list.add("雷军");
list.add("刘强东");
for (String name : list) {
System.out.println(name);
}
这是一段非常简单的集合遍历操作:对集合中的每一个字符串都进行打印输出操作。
循环遍历的弊端
Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合匿名内部类进行了对比说明。现在,我们仔细体会一下上例代码,可以发现:
for循环的语法就是“怎么做”
for循环的循环体才是“做什么”(这才是我们要完成的目标!)
如果觉得上面那样的for循环也无所谓的话,我们来完成几个需求:
1)找出姓马的;
2)再从姓马的中找到名字长度等于3的。
如何实现?在Java 8之前的做法可能为:
@Test
public void testWithFor() throws Exception {
List list = new ArrayList<>();
list.add("马云");
list.add("马化腾");
list.add("李彦宏");
list.add("雷军");
list.add("刘强东");
List maList = new ArrayList<>();
for (String name : list) {
if (name.startsWith("马")) {
maList.add(name);
}
}
List length3List = new ArrayList<>();
for (String name : maList) {
if (name.length() == 3) {
length3List.add(name);
}
}
for (String name : length3List) {
System.out.println(name);
}
}
这段代码中含有三个循环,每一个作用不同:
1)首先筛选所有姓张的人;
2)然后筛选名字有三个字的人;
3)最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。那Stream能给我们带来怎样更加优雅的写法呢?
Stream的优雅写法
下面来看一下如果使用Java 8的Stream API来实现将有多么的优雅:
@Test
public void testWithStream() throws Exception {
List list = new ArrayList<>();
list.add("马云");
list.add("马化腾");
list.add("李彦宏");
list.add("雷军");
list.add("刘强东");
list.stream()
.filter(s -> s.startsWith("马"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
}
使用Stream API将我们真正想要做的事情直接体现在代码中。直接阅读代码的字面意思即可完美展示我们要做的事:
获取流、过滤出姓马的、过滤出名字长度为3的、逐一打印。
代码如此简洁,直观,优雅!