Stream接口的使用

IntStream.of(1, 2, 3, 4, 5, 6).forEach(System.out::println);

这个直接使用集合的forEach也是可以的,使用stream除了有原始类型版本减少拆包装包以外,还可以实现并行化:

IntStream.of(1, 2, 3, 4, 5, 6).parallel().forEach(System.out::println);
   System.out.println();
   IntStream.of(1, 2, 3, 4, 5, 6).parallel().forEach(System.out::println);

输出:

  4
  5
  1
  2
  6
  3

  4
  1
  6
  2
  3
  5

可见,使用了多线程并行处理stream,每个元素被调用的顺序是不确定的。stream还提供了一个顺序的forEachOrdered,对于串行stream来说,这个方法和forEach相同,而对于并行
stream则会强制按顺序执行,如:

 IntStream.of(1, 2, 3, 4, 5, 6).parallel().forEachOrdered(System.out::println);

输出:
  
   1
   2
   3
   4
   5
   6

同样的道理,findFirst和findAny对于串行steam来说是一样的,而对于并行stream则不同:

 System.out.println(Stream.of(1, 2, 3, 4, 5, 6).findFirst());
      System.out.println(Stream.of(1, 2, 3, 4, 5, 6).findAny());

输出
    Optional[1]
    Optional[1]


再试下并行stream:

 System.out.println(Stream.of(1, 2, 3, 4, 5, 6).parallel().findFirst());
 System.out.println(Stream.of(1, 2, 3, 4, 5, 6).parallel().findAny());
 System.out.println(Stream.of(1, 2, 3, 4, 5, 6).parallel().findAny());
 System.out.println(Stream.of(1, 2, 3, 4, 5, 6).parallel().findAny());

输出
    Optional[1]
    Optional[4]
    Optional[4]
    Optional[2]


findFirst和findAny返回一个Optional<T>(对于原始类型stream,是返回OptionalInt,
OptionalLong,OptionalDouble), 因为对于空的stream,这两个方法需要返回一个找不到元素的语义(有别于null),得到Optional后可以用isPresent判断元素是否存 在,用get方法获得真正的值(如果元素不存在,调用get方法会抛出NoSuchElementException)。不过这个Optional有些坑 爹:
  
    Stream.of(null,1,2,3).findFirst();

执行结果是抛出java.lang.NullPointerException,原因是Optional构造时不允许传入null,这里我没明白既然Optional用来区别null和present的语义,为什么又不能传入null。
findFirst和findAny和filter方法配合可以实现获得满足条件的第一个元素(或者任意一个元素),例如我们要找第一个负数,可以这么写:

  stream.filter(x -> x < 0).findFirst();


接着测试Stream的惰性加载特性:

   List<Integer> list = new AbstractList<Integer>()
        {
            @Override
            public Integer get(int index)
            {
                System.out.println(index);
                return index;
            }

            @Override
            public int size()
            {
                return 3;
            }
        };

        Stream<Integer> stream = list.stream();

        System.out.println("get stream");

        stream.forEach(System.out::println);

输出:

get stream
0
0
1
1
2
2

可见构造stream的时候 并不会访问List中的元素,等调用消费方法的时候,真正需要使用到某个元素再去访问。与iterator不同的是,构造iterator之后,如果 list被修改,再使用stream,并不会抛出ConcurrentModificationException,例如:

  List<Integer> list = new ArrayList<>();

        list.add(1);
        list.add(2);

        Stream<Integer> stream = list.stream();

        list.add(3);

        stream.forEach(System.out::println);

输出:

  1
  2
  3



同理,测试映射方法的惰性加载特性:

    

   List<Integer> list = new AbstractList<Integer>()
        {
            @Override
            public Integer get(int index)
            {
                System.out.println(index);
                return index;
            }

            @Override
            public int size()
            {
                return 3;
            }
        };

        Stream<Integer> stream = list.stream().map(x -> {
            System.out.println(x);
            return x * 2;
        });

        System.out.println("get stream");

        stream.forEach(x -> {
            System.out.println(x);
            System.out.println();
        });

输出:
  
get stream
0
0
0

1
1
2

2
2
4


对于不需要读到的元素,则连加载都不会加载:

     

   List<Integer> list = new AbstractList<Integer>()
        {
            @Override
            public Integer get(int index)
            {
                System.out.println(index);
                return index;
            }

            @Override
            public int size()
            {
                return 3;
            }
        };

        Stream<Integer> stream = list.stream().limit(1);

        System.out.println("get stream");

        stream.forEach(System.out::println);

输出:

get stream
0
0

可见,只会去加载第一个元素。

也可以用peek方法来监控stream中元素的读取:

 Stream.of(1, 2, 3, 4).peek(x -> System.out.print(x + " ")).map(x -> x * 2)
        .forEach(System.out::println);


输出:

1 2
2 4
3 6
4 8

对于sorted方法,情况和其它映射方法略有不同:

  Stream<Integer> stream = Stream.of(3, 1, 2, 4)
                        .peek(System.out::println).sorted();

        System.out.println("get sorted stream");

        stream.forEach(x -> System.out.println("   " + x));

输出:

get sorted stream
3
1
2
4
   1
   2
   3
   4

从输出结果可以看出,虽然是开始消费sorted stream的时候才会去访问原stream中的元素,不过在消费一开始的时候,所有元素就被遍历一遍了,这是因为只有把所有元素取出来才能排序,会消耗一个O(n)的额外空间。


测试Stream的一次性特性:

        IntStream stream = IntStream.of(1, 2, 3, 4 - 1, 5);

        System.out.println(stream.sum());
        System.out.println(stream.anyMatch(x -> x < 0));

调用anyMatch的时候抛出异常:
java.lang.IllegalStateException: stream has already been operated upon or closed

调用映射方法后,原来的stream不再可用,如:

        IntStream stream = IntStream.of(1, 2, 3, 4 - 1, 5);

        stream.limit(1);
        System.out.println(stream.sum());

抛出异常:
java.lang.IllegalStateException: stream has already been operated upon or closed

源stream关闭后,用映射方法构造出来的stream也不再可用。

        IntStream stream = IntStream.of(1, 2, 3, 4 - 1, 5);

        IntStream limit = stream.limit(1);
        stream.close();
        System.out.println(limit.sum());

抛出异常:java.lang.IllegalStateException: source already consumed or closed

当然,一般我们使用stream的时候都是链式调用,不会犯上面的错误。

一个stream被close后,相关的stream都会被close

         Stream.of(1, 2, 3, 4, 5, 6).onClose(() -> System.out.println("close1"))
               .limit(1).onClose(() -> System.out.println("close2")).close();

输出:
close1
close2

        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        stream.onClose(() -> System.out.println("close1")).limit(1)
              .onClose(() -> System.out.println("close2"));

        stream.close();

输出:
close1
close2

转载于:https://my.oschina.net/u/1412027/blog/225092

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值