概述
四、Stream使用详解
好了,背景知识介绍完成,并且我们在最开始也对Stream有了一个大致的了解,在本章中我们详细的介绍一下Stream,每一个示例都会有相应的代码配合,让读者理解更加的透彻。
对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect),当我们使用流的时候,通常会包括三个基本的步骤:
v 获取数据
v 转换数据,每次的转换不会改变原有Stream而是会返回一个新的Stream,并且转换可以有多次的转换。
v 执行操作获取想要的结果
4.1 如何获得Stream
获取流的方式有很多种,我们在这本节中,逐个的介绍,我将代码都放在junit测试代码中。
4.1.1 From Collections
@Test public void fromCollections() { List<String> list = Collections.emptyList(); Stream<String> stream = list.stream(); Stream<String> parallelStream = list.parallelStream(); } |
4.1.2 From Arrays
@Test public void fromArrays() { int[] array = {1,2,3,4,5}; IntStream stream = Arrays.stream(array); Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6); } |
4.1.3 From Static Factory
@Test public void fromArrays() { int[] array = {1,2,3,4,5}; IntStream stream = Arrays.stream(array); Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6); } |
4.1.4 From Files
@Test public void fromFiles() { Path path = Paths.get(""); try { Stream<Path> stream = Files.walk(path); } catch (IOException e) { e.printStackTrace(); } } |
4.1.5 Build By Yourself
除了一些常见的方式获取Stream之外,我们还可以自己构造如何获取一个Stream,这种情形通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的 Stream。把 Supplier 实例传递给 Stream.generate() 生成的 Stream,默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。
@Test public void generateByYourself() { Random random = new Random(System.currentTimeMillis()); Supplier<Integer> supplierRandom = random::nextInt; Stream.generate(supplierRandom).limit(10).forEach((e)->System.out.println(e)); } |
好,来个更加复杂的,我们自定义一个Supplier,来演示一下如何自己generate一个Stream,为了方便代码展示,我写了两个局部内部类,都在函数内部定义的两个类,代码如下所示:
@Test public void generateByExpandSupplier() { class Member { private int id; private String name;
public Member(int id, String name) { Member.this.id = id; Member.this.name = name; }
public int getId() { return id; }
public String getName() { return name; } }
class MemberSupplier implements Supplier<Member> {
private int index = 0; private Random random = new Random(System.currentTimeMillis());
@Override public Member get() { return new Member(index = random.nextInt(100), "Member#" + index); } }
Stream.generate(new MemberSupplier()).limit(20).forEach((m) -> System.out.println(m.getId() + ":" + m.getName())); } |
运行结果如下所示:
42:Member#42 30:Member#30 38:Member#38 12:Member#12 15:Member#15 ........... |
4.1.6 Others
v java.util.Spliterator
v Random.ints()
v BitSet.stream()
v Pattern.splitAsStream(java.lang.CharSequence)
v JarFile.stream()
4.2 Stream的操作分类
掌握了如何获取Stream,我们本节中来看看如何对Stream进行操作,Stream的操作大致分为两类
Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
还有一种操作被称为 short-circuiting。用以指:
对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件。
4.3 Stream的操作实战
了解了Stream的分类,本节中我们一起来实战一下对其所涉及的每个方法进代码演示。
4.3.1 Intermediate操作
Intermediate的操作方法大致有如下几个:
v map (mapToInt, flatMap 等)
v Filter
v Distinct
v Sorted
v Peek
v Limit
v Skip
v Parallel
v Sequential
v unordered
4.3.1.1 map
@Test public void testMap() { int[] array = {1, 2, 3, 4, 5}; IntStream.of(array).map(e -> e * 10).forEach((e) -> System.out.println(e)); } |
上面的代码是对每一个数组的元素都增加了十倍并且打印出来。
4.3.1.2 filter
@Test public void testFilter() { Integer[] array = {1, 2, 3, 4, 5}; List<Integer> result = Stream.of(array).filter(e -> e > 3).collect(Collectors.toList()); assertEquals(2,result.size()); } |
上面的代码是过滤掉比3小的数据,并且形成了一个新的Integer列表。
4.3.1.3 Distinct
@Test public void testDistinct() { Integer[] array = {1, 1, 2, 3, 4, 5, 6, 5}; assertEquals(8, array.length); List<Integer> result = Stream.of(array).distinct().collect(Collectors.toList()); assertEquals(6, result.size()); } |
上面的代码过滤掉了重复的元素。
4.3.1.4 Sorted
@Test public void testSorted() { Integer[] array = {3,4,6,1,8,2}; Stream.of(array).sorted().forEach(e->System.out.println(e)); } |
Sorted是对流中的元素进行升序排列,并且形成一个新的流,如果你想有自己的排序规则,请实现Compare接口并且传给sorted。
4.3.1.5 Peek
@Test public void testPeek() { Integer[] array = {3,4,6,1,8,2}; List<Integer> result = Stream.of(array).filter(e -> e % 2 == 0).peek(e -> System.out.println(e)).collect(Collectors.toList()); System.out.println(result); } |
Peek有点类似于forEach,但是他不会中断这个Stream,体会一下上面的代码。
4.3.1.6 Limit
@Test public void testLimit() { Integer[] array = {3,4,6,1,8,2}; Stream.of(array).limit(4).forEach(e -> System.out.println(e)); } |
只获取流中的前四个元素,并且形成一个新的流返回。
4.3.1.7 Skip
@Test public void testSkip() { Integer[] array = {3,4,6,1,8,2}; Stream.of(array).skip(3).forEach(e -> System.out.println(e)); } |
Limit是保留前面的几个元素形成一个新的,而skip则是跳过前面的几个元素形成一个新的流。
4.3.1.8 Parallel
@Test public void testParallel() { Integer[] array = {3,4,6,1,8,2}; Stream.of(array).parallel().forEach(e -> System.out.println(e)); } |
产生一个并行计算的流并且返回。
4.3.1.9 Sequential
@Test public void testDistinct() { Integer[] array = {1, 1, 2, 3, 4, 5, 6, 5}; assertEquals(8, array.length); List<Integer> result = Stream.of(array).distinct().collect(Collectors.toList()); assertEquals(6, result.size()); } |
和Parallel相反,返回一个串行执行的Stream,有可能返回的是自己(this)。
4.3.2 Terminal
我们之前说过了Terminal会中断整个流的操作,Stream的Terminal操作有如下几个,有些我们已经演示过了,就不给出代码占用版面了。
v forEach
v forEachOrdered
v toArray
v Reduce
v Collect
v Min
v Max
v Count
v anyMatch
v allMatch
v noneMatch
v findFirst
v findAny
v iterator
4.3.2.1 toArray
@Test public void testForEachOrdered() { Integer[] array = {1, 2, 3, 4, 5, 6}; Object[] result = Stream.of(array).filter(e -> e > 4).toArray(); assertEquals(2,result.length); } |
返回一个新的数组。
4.3.2.2 reduce
这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于
Integer sum = integers.reduce(0, (a, b) -> a+b); 或
Integer sum = integers.reduce(0, Integer::sum);
也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。
@Test public void testReduce() { String reduceResult = Stream.of("W", "a", "n", "g").reduce("", String::concat); assertEquals("Wang",reduceResult); } |
4.3.2.3 max,min,count,sum
比较简单,不演示了,猜都能猜出来。
4.3.2.4 anyMatch
Stream中的元素只要有一个满足条件则就返回true,否则返回false
@Test public void testAnyMatch() { Integer[] array = {1, 2, 3, 4, 5}; boolean result = Stream.of(array).anyMatch(e -> e > 3); assertTrue(result); } |
4.3.2.5 allMatch
所有的都必须满足条件
4.3.2.6 noneMatch
所有的都不满足条件
4.3.2.7 findFirst
@Test public void testFindFirst() { Integer[] array = {1, 2, 3, 4, 5}; Integer result = Stream.of(array).findFirst().get(); assertEquals(1,result.intValue()); } |
返回流中的第一个满足条件的数据返回。
4.3.2.8vfindAny
和FindFirst一样都是返回一个数据,但是他是返回任意的一个元素,所以之前我们的断言方式有可能会失败。v
@Test public void testFindAny() { Integer[] array = {1, 2, 3, 4, 5}; Integer result = Stream.of(array).findAny().get(); System.out.println(result); } |
4.3.2.9 iterator
@Test public void testIterator() { Integer[] array = {1, 2, 3, 4, 5}; Iterator<Integer> iterator = Stream.of(array).iterator(); } |
返回我们熟悉的迭代器Iterator,不做解释,相信你会明白吧。
完整文档的下载地址为:http://download.csdn.net/detail/wangwenjun69/8981633
最后
以上就是怕孤独方盒为你收集整理的Java8之Stream(3)四、Stream使用详解的全部内容,希望文章能够帮你解决Java8之Stream(3)四、Stream使用详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复