Java8 Stream

ref:http://crazy-j.iteye.com/blog/2041400

JDK8不久前正式发布了,这一次更新为我们带来了相当多的东西,其中一个非常庞大也是非常复杂的框架Stream,特别值得一提,他可以将一些让人烦躁的事情变得简洁,好,进入正题。

 

现在需要除去一个无序数组中大于800的数

 

传统方法:

Java代码  收藏代码
  1. for(int i = 0; i < list.size(); i++) {  
  2.     if(list.get(i) > 800) {  
  3.         list.remove(i);  
  4.     }  
  5. }  

 使用Stream与lambda

Java代码  收藏代码
  1. list.stream().filter((n) -> n <= 800);  

 

如此痛快已经显而易见了,但对于没有了解lambda表达式的童鞋们对类似代码有些抵触,接下来我们还使用传统的Java代码介绍几个比较常用的API。

Java代码  收藏代码
  1. public static void learnMore(List<Integer> list) {  
  2.     // 创建流式调用对象  
  3.     list.stream()  
  4.       
  5.     // 过滤,只保留值大于800的元素。  
  6.     .filter(new Predicate<Integer>() {  
  7.   
  8.         @Override  
  9.         public boolean test(Integer t) {  
  10.             return t > 800;  
  11.         }  
  12.           
  13.     })  
  14.       
  15.     // 排序,默认升序,可传入Comparator。  
  16.     .sorted()  
  17.       
  18.     // 通过map方法将集合中的对象转换为String类型  
  19.     .map(new Function<Integer, String>() {  
  20.         @Override  
  21.         public String apply(Integer t) {  
  22.             return new StringBuilder("String[").append(t).append("]").toString();  
  23.         }  
  24.     })  
  25.       
  26.     // 获取集合中最多n个元素。  
  27.     .limit(10)  
  28.       
  29.     .forEach(new Consumer<String>() {  
  30.         @Override  
  31.         public void accept(String t) {  
  32.             System.out.println(t);  
  33.         }  
  34.     });  
  35.       
  36.     System.out.println("List size:" + list.size());  
  37. }  

 程序输出:

String[801]

String[802]

String[803]

String[804]

String[805]

String[806]

String[807]

String[808]

String[809]

String[810]

List size:1000

通过输出我们可以看到整个流程中所有的操作得到了处理,且所有操作均没有涉及对原集合的变动,探查源代码发现stream对象是靠原集合创建迭代器进行所有操作,因此不对原集合内容进行改动。

 

接下来我们看一个比较有意思的事情:

Java代码  收藏代码
  1. public static void whyNoPrint(List<Integer> list) {  
  2.     list.stream()  
  3.       
  4.     .filter(new Predicate<Integer>() {  
  5.   
  6.         @Override  
  7.         public boolean test(Integer t) {  
  8.             System.out.println(t);  
  9.             return true;  
  10.         }  
  11.     });  
  12. }  

该方法执行过后程序没有任何输出,其实本人最初并不知道这个细节,而是在研究另外一个事情的时候发现的,稍微想一想就能看出这个程序有问题,流对象在filter后就不做任何事情了,所以这个筛选操作也失去了意义,应该是JDK在设计时考虑到的细节,非常值得我们学习。

 

接下来的东西实在是让人太兴奋了:并行流

这个东西是在研究stream.reduce方法时候发现的,reduce提供三个方法的重载,分别为1个参数,两个参数与三个参数,在尝试使用三个参数的reduce方法时候我发现了一个问题,就是最后一个参数传入的BinaryOperator.apply方法根本没有被调用!通过一番苦苦的读源代码工作后我发现reduce方法的第三个参数仅仅在stream以并行模式工作时才会生效!为什么?他是做什么的?我们来看下面的代码:

Java代码  收藏代码
  1. public static void learnReduce_3(List<Integer> list) {  
  2.     // 并行流  
  3.     Stream<Integer> parallelStream = list.stream().parallel();  
  4.     // 串行流  
  5.     Stream<Integer> sequentialStream = list.stream();  
  6.       
  7.     long start = System.currentTimeMillis();  
  8.     parallelStream.reduce(0new BiFunction<Integer, Integer, Integer>() {  
  9.   
  10.         @Override  
  11.         public Integer apply(Integer t, Integer u) {  
  12.             // 在这里停顿10毫秒来提高并行与串行计算的差异。  
  13.             // 虽然流提供了并行计算支持,但如果你需要处理的问题非常简单,还是推荐使用串行模式。  
  14.             // 因为那样避免了并行计算自身的开销,会更快。  
  15.             try {  
  16.                 Thread.sleep(10);  
  17.             } catch (InterruptedException e) {  
  18.             }  
  19.             return t + u;  
  20.         }  
  21.     }, new BinaryOperator<Integer>() {  
  22.   
  23.         @Override  
  24.         public Integer apply(Integer left, Integer right) {  
  25.             System.out.println(Thread.currentThread().getName());  
  26.             return left + right;  
  27.         }  
  28.     });  
  29.     System.out.println("Parallel stream processed with " + (System.currentTimeMillis() - start) + "ms.");  
  30.       
  31.     start = System.currentTimeMillis();  
  32.     sequentialStream.reduce(0new BiFunction<Integer, Integer, Integer>() {  
  33.   
  34.         @Override  
  35.         public Integer apply(Integer t, Integer u) {  
  36.             try {  
  37.                 Thread.sleep(10);  
  38.             } catch (InterruptedException e) {  
  39.             }  
  40.             return t + u;  
  41.         }  
  42.     }, new BinaryOperator<Integer>() {  
  43.   
  44.         @Override  
  45.         public Integer apply(Integer t, Integer u) {  
  46.             System.out.println("Can't print this line.");  
  47.             return 0;  
  48.         }  
  49.     });  
  50.     System.out.println("Sequential stream processed with " + (System.currentTimeMillis() - start) + "ms.");  
  51. }  

 程序输出:

ForkJoinPool.commonPool-worker-3

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-2

main

ForkJoinPool.commonPool-worker-6

ForkJoinPool.commonPool-worker-4

ForkJoinPool.commonPool-worker-1

ForkJoinPool.commonPool-worker-1

ForkJoinPool.commonPool-worker-5

ForkJoinPool.commonPool-worker-3

ForkJoinPool.commonPool-worker-3

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-2

ForkJoinPool.commonPool-worker-2

ForkJoinPool.commonPool-worker-2

ForkJoinPool.commonPool-worker-5

ForkJoinPool.commonPool-worker-5

ForkJoinPool.commonPool-worker-5

ForkJoinPool.commonPool-worker-5

ForkJoinPool.commonPool-worker-4

ForkJoinPool.commonPool-worker-4

ForkJoinPool.commonPool-worker-4

main

main

ForkJoinPool.commonPool-worker-6

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-7

ForkJoinPool.commonPool-worker-7

Parallel stream processed with 1350ms.

 

Sequential stream processed with 10003ms.

 

通过线程名称可以看出,流以并行模式运行的时候使用到了ForkJoin框架。

那么就更加明确了,当流在并行模式下工作时,JDK使用了多个线程进行并行处理,这将牵扯到多个计算结果合并的问题,所以reduce方法的第三个参数(BinaryOperator.apply)作用是将两个线程的计算结果合并。

 

更多信息参考:http://www.infoq.com/cn/articles/forkjoin-to-parallel-streams

### Java 8 Stream API 使用教程 #### 创建流 创建流的方式有多种,可以通过集合类的 `stream()` 方法获取流实例。 ```java List<String> list = Arrays.asList("apple", "banana", "orange"); Stream<String> stream = list.stream(); ``` 对于数组,则可以使用静态方法 `Arrays.stream` 或者 `Stream.of`. ```java String[] array = {"apple", "banana", "orange"}; Stream<String> streamFromArray = Arrays.stream(array); // or Stream<String> streamOfArray = Stream.of(array); ``` #### 中间操作 中间操作返回一个新的流对象,在此之后还可以继续调用其他的操作。常见的中间操作包括但不限于: - **filter**: 过滤掉不符合条件的元素[^1] ```java Stream<String> filteredStream = stream.filter(s -> s.startsWith("a")); ``` - **map**: 将每一个输入元素按照指定映射关系转换成另一个输出元素 ```java Stream<Integer> lengthStream = stream.map(String::length); ``` - **peek**: 对每个元素执行某些动作并传给下游,通常用于调试目的[^2] ```java Stream<String> peekedStream = stream.peek(System.out::println); ``` #### 终端操作 终端操作会触发实际计算过程,并关闭流。一旦执行了一个终端操作后就不能再对该流做任何进一步的操作了。一些常用的终端操作如下所示: - **forEach**: 遍历整个序列中的每一项 ```java stream.forEach(System.out::println); ``` - **collect(Collectors.toList())**: 收集结果到列表中 ```java List<String> resultList = stream.collect(Collectors.toList()); ``` - **count()**: 计算总数目 ```java long count = stream.count(); ``` 这些只是基础部分;实际上还有更多高级特性和组合方式等待探索。通过合理运用上述概念以及所提供的例子,应该能更好地理解如何利用Java Streams简化日常开发工作流程的同时提高效率和代码质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值