Java8之Stream(3)

四、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

猜你喜欢

转载自blog.csdn.net/wangwenjun69/article/details/47400817