Stream是jdk1.8加入的新功能,在它的接口注释里是这么描述:Stream支持对一系列数据进行顺序和并行聚合操作。从形式上看,Stream以fluent的风格组织代码,让逻辑非常紧凑。但只是代码风格的差异,我相信就会人产生迷惑,直接用for循环处理集合数据,不也挺直观的?
的确,我开始也是这么认为,Stream只是内置一个迭代器,是对for结构的封装,两者没有本质的差别。随着深入理解,我发现它们体现的是两种不同的编程思想。
所以我理解Stream设计的初衷是把关注点从逻辑处理转移到数据的变化上来,并且简化数据操作、降低并行聚合的难度。
下面举个例子来描述两者的差异。
比如,我们要从1-10的数字里,找到3的倍数,自然排序后,按照固定格式打印出来。
不使用Stream的代码
List list = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
List sorted = new ArrayList<>();
for (Integer num : list) {if (num % 3 == 0) {sorted.add(num);}
}
Collections.sort(sorted);
StringJoiner forStrJoiner = new StringJoiner(",", "{", "}");
for (Integer num : sorted) {forStrJoiner.add(String.valueOf(num));
}
System.out.println("forStr = " + forStrJoiner);
可以发现,需求描述很简洁,1.找到3的倍数,2.自然排序 3.按照固定格式打印。但是写出来的代码并不是和需求一一对应。
例如代码里的for循环结构是编程语法,不是需求的业务语言,在理解代码的业务含义时,属于噪音。
使用Stream的代码
String streamStr = list.stream().filter(it -> it % 3 == 0).sorted().map(it -> String.valueOf(it)).collect(Collectors.joining(",", "{", "}"));
System.out.println("streamStr = " + streamStr);
Stream的代码描述的很清晰,不仅仅是代码少了,而是Stream是按照<数据,操作名,回调函数>三元组来定义一个操作。Stream的操作和需求描述基本是一一对应,所以可读性很强。
【找到】 -> filter()
【自然排序】 -> sorted()
【格式化】 -> joining()
在项目代码里,会有功能的业务处理逻辑,局部也会有数据处理逻辑。 Stream则是针对数据处理的场景,制定了常用的标准操作。 通过这些操作让我们更容易处理数据,包括写出更易理解的代码,更方便地使用多线程并行处理的能力。