课程咨询 :0592-5903858 QQ:1079585464

厦门达内java培训

厦门Java培训 > 达内新闻 > 达内详解Java Stream类的特点
  • 达内详解Java Stream类的特点

    发布:厦门Java培训      来源:码农网      时间:2016-03-08


  •     Stream是 Java 8新增加的类,用来补充集合类。 厦门达内Java培训专家全面介绍Java Stream的特点、以及Java Stream的第三方扩展。

    一、介绍

        Stream接口还包含几个基本类型的子接口如IntStream, LongStream 和 DoubleStream。

        达内Java培训专家介绍下流和其它集合具体的区别:

        1.不存储数据 。流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。

        2.函数式编程 。流的操作不会修改数据源,例如filter不会将数据源中的数据删除。

        3.延迟操作 。流的很多操作如filter,map等中间操作是延迟执行的,只有到终点操作才会将操作顺序执行。

        4.可以解绑 。对于无限数量的流,有些操作是可以在有限的时间完成的,比如limit(n)或findFirst(),这些操作可是实现”短路”(Short-circuiting),访问到有限的元素后就可以返回。

        5.纯消费 。流的元素只能访问一次,类似Iterator,操作没有回头路,如果你想从头重新访问流的元素,对不起,你得重新生成一个新的流。

        流的操作是以管道的方式串起来的。流管道包含一个数据源,接着包含零到N个中间操作,最后以一个终点操作结束。

    并行 Parallelism

        所有的流操作都可以串行执行或者并行执行。除非显示地创建并行流,否则Java库中创建的都是串行流。Collection.stream()为集合创建串行流而Collection.parallelStream()为集合创建并行流。IntStream.range(int, int)创建的是串行流。通过parallel()方法可以将串行流转换成并行流,sequential()方法将流转换成并行流。

        除非方法的Javadoc中指明了方法在并行执行的时候结果是不确定(比如findAny、forEach),否则串行和并行执行的结果应该是一样的。

    不干涉 Non-interference

        流可以从非线程安全的集合中创建,当流的管道执行的时候,非concurrent数据源不应该被改变。下面的代码会抛出java.util.ConcurrentModificationException异常:


    List<String> l = new ArrayList(Arrays.asList("one", "two"));Stream<String> sl = l.stream();sl.forEach(s -> l.add("three"));


        在设置中间操作的时候,可以更改数据源,只有在执行终点操作的时候,才有可能出现并发问题(抛出异常,或者不期望的结果),比如下面的代码不会抛出异常:


    List<String> l = new ArrayList(Arrays.asList("one", "two"));Stream<String> sl = l.stream();l.add("three");sl.forEach(System.out::println);


        对于concurrent数据源,不会有这样的问题,比如下面的代码很正常:


    List<String> l = new CopyOnWriteArrayList<>(Arrays.asList("one", "two"));Stream<String> sl = l.stream();sl.forEach(s -> l.add("three"));


        虽然我们上面例子是在终点操作中对非并发数据源进行修改,但是非并发数据源也可能在其它线程中修改,同样会有并发问题。

    无状态 Stateless behaviors

        大部分流的操作的参数都是函数式接口,可以使用Lambda表达式实现。它们用来描述用户的行为,称之为行为参数(behavioral parameters)。

        如果这些行为参数有状态,则流的操作的结果可能是不确定的,比如下面的代码:


    List<String> l = new ArrayList(Arrays.asList("one", "two", ……));class State {    boolean s;}final State state = new State();Stream<String> sl = l.stream().map(e -> {    if (state.s)        return "OK";    else {        state.s = true;        return e;    } });sl.forEach(System.out::println);


        上面的代码在并行执行时多次的执行结果可能是不同的。这是因为这个lambda表达式是有状态的。

    副作用 Side-effects

        有副作用的行为参数是被鼓励使用的。

        副作用指的是行为参数在执行的时候有输入输入,比如网络输入输出等。

        这是因为Java不保证这些副作用对其它线程可见,也不保证相同流管道上的同样的元素的不同的操作运行在同一个线程中。

        很多有副作用的行为参数可以被转换成无副作用的实现。一般来说println()这样的副作用代码不会有害。


    ArrayList<String> results = new ArrayList<>();stream.filter(s -> pattern.matcher(s).matches())      .forEach(s -> results.add(s));  // 


        副作用代码上面的代码可以改成无副作用的。


    List<String>results =    stream.filter(s -> pattern.matcher(s).matches())          .collect(Collectors.toList());  // No side-effects!


    排序 Ordering

        某些流的返回的元素是有确定顺序的,我们称之为encounter order。这个顺序是流提供它的元素的顺序,比如数组的encounter order是它的元素的排序顺序,List是它的迭代顺序(iteration order),对于HashSet,它本身就没有encounter order。

       一个流是否是encounter order主要依赖数据源和它的中间操作,比如数据源List和Array上创建的流是有序的(ordered),但是在HashSet创建的流不是有序的。

        sorted()方法可以将流转换成有序的,unordered可以将流转换成无序的。除此之外,一个操作可能会影响流的有序,比如map方法,它会用不同的值甚至类型替换流中的元素,所以输入元素的有序性已经变得没有意义了,但是对于filter方法来说,它只是丢弃掉一些值而已,输入元素的有序性还是保障的。

        对于串行流,流有序与否不会影响其性能,只是会影响确定性(determinism),无序流在多次执行的时候结果可能是不一样的。

        对于并行流,去掉有序这个约束可能会提供性能,比如distinct、groupingBy这些聚合操作。

    结合性 Associativity

        一个操作或者函数op满足结合性意味着它满足下面的条件:

    (a op b) op c == a op (b op c)

        对于并发流来说,如果操作满足结合性,我们就可以并行计算:

    a op b op c op d == (a op b) op (c op d)

        比如min、max以及字符串连接都是满足结合性的。

    二、创建Stream

        可以通过多种方式创建流:

        1、通过集合的stream()方法或者parallelStream(),比如Arrays.asList(1,2,3).stream()。

        2、通过Arrays.stream(Object[])方法, 比如Arrays.stream(new int[]{1,2,3})。

        3、使用流的静态方法,比如Stream.of(Object[]),IntStream.range(int, int)或者Stream.iterate(Object, UnaryOperator),如Stream.iterate(0, n -> n * 2),或者generate(Supplier<T> s)如Stream.generate(Math::random)。

        4、BufferedReader.lines()从文件中获得行的流。

        5、Files类的操作路径的方法,如list、find、walk等。

        6、随机数流Random.ints()。

        7、其它一些类提供了创建流的方法,如BitSet.stream(),Pattern.splitAsStream(java.lang.CharSequence), 和JarFile.stream()。

        8、更底层的使用StreamSupport,它提供了将Spliterator转换成流的方法。

    三、Stream的第三方扩展

        一些开源项目为stream提供了额外的一些操作,比如 protonpack 项目提供了下列方法:

    takeWhile and takeUntil
    skipWhile and skipUntil
    zip and zipWithIndex
    unfold
    MapStream
    aggregate
    Streamable
    unique collector

        java8-utils 也提供了一些有益的辅助方法。





    原文链接:http://www.codeceo.com/article/java-stream-usage.html
    推荐文章

上一篇:Java要如何使用堆外内存?

下一篇:java项目实战之答答租车代码

最新开班日期  |  更多

Java--大数据周末班

Java--大数据周末班

开班日期:每周一

Java--大数据全日制班

Java--大数据全日制班

开班日期:每周一

Java--零基础周末班

Java--零基础周末班

开班日期:每周一

Java--零基础全日制班

Java--零基础全日制班

开班日期:每周一

  • 地址:厦门软件园二期望海路59号之一401达内科技
  • 课程培训电话:0592-5903858 QQ:1079585464     全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56