美文网首页
java8-Stream-构建流

java8-Stream-构建流

作者: 表象_Dark | 来源:发表于2017-12-22 16:58 被阅读0次

stream 基础定义

  • 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序 值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元 素(如ArrayList 与 LinkedList)。但流的目的在于表达计算,比如你前面见到的 filter、sorted和map。集合讲的是数据,流讲的是计算。
  • 源——流会使用一个提供数据的源,如集合、数组或输入/输出资源。
  • 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中 的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执 行,也可并行执行。
    此外,流操作有两个重要的特点。
  • 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大
    的流水线。这让我们下一章中的一些优化成为可能,如延迟和短路。流水线的操作可以看作对数据源进行数据库式查询。
  • 内部迭代——与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

构建流

所有的流构建方法 最终都会调用如下的这个方法

/* 
 * @param spliterator a {@code Spliterator} describing the stream elements
 * @param parallel if {@code true} then the returned stream is a parallel stream; 
 *                 if {@code false} the returned stream is a sequential stream.
 */        
public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
        Objects.requireNonNull(spliterator);
        return new ReferencePipeline.Head<>(spliterator,
                                            StreamOpFlag.fromCharacteristics(spliterator),
                                            parallel);
}
  • 由值创建流
    Stream 提供两个静态方法可以把值转换成流
方法一:
List<Integer> list = Arrays.asList(1, 2, 3);
Stream<List<Integer>> stream = Stream.of(list);

方法二:
Stream<Object> stream1 = Stream.of(1, 2, "3");

注:这两种方法返回类型 是编译器通过类型推断得出的。
使用方法二,源码中有这么一行提示:Creating a stream from an array is safe
  • 由数组创建流
List<Integer> list = Arrays.asList(1, 2, 3);
Stream<Integer> stream = list.stream();
  • 由函数生成:创建无限流
方法一:迭代
Stream<Integer> stream = Stream.iterate(0, n -> n + 2);

该迭代的源码如下:
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {
            @SuppressWarnings("unchecked")
            T t = (T) Streams.NONE;

            @Override
            public boolean hasNext() {
                return true;
            }

            @Override
            public T next() {
                return t = (t == Streams.NONE) ? seed : f.apply(t);
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iterator,
                Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}

如上所示,第二个参数为一个 lambda UnaryOperator<T>  
关于这个指令的 解释是:传入一个 参数T,返回一个参数T,如 (n -> n+2);传入 一个 (int n),返回一个 (int + 2) 类型相同,
因此,我们有的迭代方式也可以

Stream.iterate(new int[]{0, 1},t -> new int[]{t[1], t[0]+t[1]})
方法二:生成
Stream.generate(Math::random)

迭代生成源码如下:
public static<T> Stream<T> generate(Supplier<T> s) {
        Objects.requireNonNull(s);
        return StreamSupport.stream(
                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
如上所示,此函数只会接受一个 lambda supplier<T> 作为一个生产者,
由于该生产者可能会在类内部存储某些状态,但是这些状态有可能是可变的,如果在并行流中使用可能不会得到我们的期望值。
 Stream.generate(new Supplier<Object>() {
    long sum = 0;
    @Override
    public Object get() {
        return sum += 1;
    }
  });
这段代码如果在并行流中使用,由于sum 会被所有线程共用,所以这种写法是错误的。

迭代 iterate 在迭代的方法中由于每次迭代都是静态的 所以下面的这种写法是正确的:
Stream.iterate(0, new UnaryOperator<Integer>() {
   int sum = 1;
   @Override
   public Integer apply(Integer integer) {
       return sum += integer;  
   }
}).limit(5).parallel().forEach(System.out::println);

但是由于并行流的顺序不确定性,所以我们的期望值:0,1,2,4,8 可能和我们运行的结果(某次运行结果:2,8,4,1,0)并不一致。

相关文章

网友评论

      本文标题:java8-Stream-构建流

      本文链接:https://www.haomeiwen.com/subject/kczowxtx.html