资讯专栏INFORMATION COLUMN

《java 8 实战》读书笔记 -第四章 引入流

jeyhan / 3152人阅读

摘要:第四章引入流一什么是流流是的新成员,它允许你以声明性方式处理数据集合通过查询语句来表达,而不是临时编写一个实现。

第四章 引入流 一、什么是流
流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码。

下面两段代码都是用来返回低热量的菜肴名称的,并按照卡路里排序,一个是用Java 7写的,另一个是用Java 8的流写的。

之前(Java 7):

List lowCaloricDishes = new ArrayList<>(); 
for(Dish d: menu){ 
 if(d.getCalories() < 400){ 
 lowCaloricDishes.add(d); 
 } 
} 
Collections.sort(lowCaloricDishes, new Comparator() {
 public int compare(Dish d1, Dish d2){ 
 return Integer.compare(d1.getCalories(), d2.getCalories()); 
 } 
}); 
List lowCaloricDishesName = new ArrayList<>(); 
for(Dish d: lowCaloricDishes){ 
 lowCaloricDishesName.add(d.getName()); 
} 

在这段代码中,你用了一个“垃圾变量”lowCaloricDishes。它唯一的作用就是作为一次
性的中间容器。在Java 8中,实现的细节被放在它本该归属的库里了。

之后(Java 8):

import static java.util.Comparator.comparing; 
import static java.util.stream.Collectors.toList; 
List lowCaloricDishesName = 
 menu.stream() 
 .filter(d -> d.getCalories() < 400) 
 .sorted(comparing(Dish::getCalories))
 .map(Dish::getName) 
 .collect(toList()); 

为了利用多核架构并行执行这段代码,你只需要把stream()换成parallelStream()

List lowCaloricDishesName = 
 menu.parallelStream() 
 .filter(d -> d.getCalories() < 400) 
 .sorted(comparing(Dishes::getCalories)) 
 .map(Dish::getName) 
 .collect(toList()); 

按照Map里面的类别对菜肴进行分组。

Map> dishesByType = menu.stream().collect(groupingBy(Dish::getType)); 
其他库:Guava、Apache和lambdaj 
为了给Java程序员提供更好的库操作集合,前人已经做过了很多尝试。比如,Guava就是谷歌创建的一个很流行的库。它提供了multimaps和multisets等额外的容器类。Apache Commons Collections库也提供了类似的功能。Mario Fusco编写的lambdaj受到函数式编程的启发,也提供了很多声明性操作集合的工具。

流到底是什么呢?简短的定义就是“从支持数据处理操作的源生成的元素序列”。

元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList 与 LinkedList)。但流的目的在于表达计算,比如你前面见到的filter、sorted和map。集合讲的是数据,流讲的是计算。我们会在后面几节中详细解释这个思想。

——流会使用一个提供数据的源,如集合、数组或输入/输出资源。 请注意,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。

数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。

eg: 选出3条卡路里高于300的菜肴的菜肴的名字

import static java.util.stream.Collectors.toList; 
List threeHighCaloricDishNames = 
 menu.stream() 
 .filter(d -> d.getCalories() > 300)
 .map(Dish::getName)
 .limit(3)         //筛选(filter)、提取(map)或截断(limit)
 .collect(toList()); 
System.out.println(threeHighCaloricDishNames); 
//在调用collect之前,没有任何结果产生,实际上根本就没有从menu里选择元素
二、流与集合

和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。

使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。 相反,Streams库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。

三、流操作

java.util.stream.Stream中的Stream接口定义了许多操作。它们可以分为两大类:

中间操作
可以连接起来的流操作称为中间操作,诸如filter或sorted等

终端操作
关闭流的操作称为终端操作,其结果是任何不是流的值,比如List、Integer,甚至void

eg:(注意循环合并)

    List names = 
     menu.stream() 
     .filter(d -> { 
     System.out.println("filtering" + d.getName()); 
     return d.getCalories() > 300; 
     }) 
     .map(d -> { 
     System.out.println("mapping" + d.getName()); 
     return d.getName(); 
     }) 
     .limit(3) 
     .collect(toList()); 
    System.out.println(names);

此代码执行时将打印:

    filtering pork 
    mapping pork 
    filtering beef 
    mapping beef 
    filtering chicken 
    mapping chicken 
    [pork, beef, chicken] 

尽管filter和map是两个独立的操作,但它们合并到同一次遍历中了(我们把这种技术叫作循环合并)。

四、使用流

流的使用一般包括三件事:

一个数据源(如集合)来执行一个查询;

一个中间操作链,形成一条流的流水线;

一个终端操作,执行流水线,并能生成结果。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77531.html

相关文章

  • Java8实战》-读书笔记第一章(02)

    摘要:实战读书笔记第一章从方法传递到接着上次的,继续来了解一下,如果继续简化代码。去掉并且生成的数字是万,所消耗的时间循序流并行流至于为什么有时候并行流效率比循序流还低,这个以后的文章会解释。 《Java8实战》-读书笔记第一章(02) 从方法传递到Lambda 接着上次的Predicate,继续来了解一下,如果继续简化代码。 把方法作为值来传递虽然很有用,但是要是有很多类似与isHeavy...

    lushan 评论0 收藏0
  • java 8 实战读书笔记 -第五章 使用

    摘要:比如,你可以建立一个,选出热量超过卡路里的头三道菜请注意也可以用在无序流上,比如源是一个。跳过元素流还支持方法,返回一个扔掉了前个元素的流。一般来说,应该使用来对这种流加以限制,以避免打印无穷多个值。 一、筛选和切片 1.用谓词筛选 Streams接口支持filter方法。该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。例如筛选出所有...

    Richard_Gao 评论0 收藏0
  • java 8 实战读书笔记 -第七章 并行数据处理与性能

    摘要:正确使用并行流错用并行流而产生错误的首要原因,就是使用的算法改变了某些共享状态。高效使用并行流留意装箱有些操作本身在并行流上的性能就比顺序流差还要考虑流的操作流水线的总计算成本。 一、并行流 1.将顺序流转换为并行流 对顺序流调用parallel方法: public static long parallelSum(long n) { return Stream.iterate(1L...

    刘福 评论0 收藏0
  • Java8实战》-第五章读书笔记(使用Stream-01)

    摘要:跳过元素流还支持方法,返回一个扔掉了前个元素的流。归约到目前为止,我们见到过的终端操作都是返回一个之类的或对象等。这样的查询可以被归类为归约操作将流归约成一个值。通过反复使用加法,你把一个数字列表归约成了一个数字。 使用流 在上一篇的读书笔记中,我们已经看到了流让你从外部迭代转向内部迭代。这样,你就用不着写下面这样的代码来显式地管理数据集合的迭代(外部迭代)了: /** * 菜单 ...

    OldPanda 评论0 收藏0
  • Java8实战》-第六章读书笔记(用收集数据-01)

    摘要:收集器用作高级归约刚刚的结论又引出了优秀的函数式设计的另一个好处更易复合和重用。更具体地说,对流调用方法将对流中的元素触发一个归约操作由来参数化。另一个常见的返回单个值的归约操作是对流中对象的一个数值字段求和。 用流收集数据 我们在前一章中学到,流可以用类似于数据库的操作帮助你处理集合。你可以把Java 8的流看作花哨又懒惰的数据集迭代器。它们支持两种类型的操作:中间操作(如 filt...

    EscapedDog 评论0 收藏0
  • Java并发编程实战读书笔记-第1章 简介

    摘要:线程允许同一个进程中同时存在多个程序控制流。线程也被称为轻量级进程。现代操作系统中,都是以线程为基本的调度单位,而不是进程。 并发简史 在早期的计算机中不包含操作系统,从头至尾都只执行一个程序,并且这个程序能访问计算机所有资源。操作系统的出现使得计算机每次能运行多个程序,并且不同的程序都在单独的进程中运行:操作系统为各个独立执行的进程分配内存、文件句柄、安全证书等。不同进程之间通过一些...

    zhoutk 评论0 收藏0

发表评论

0条评论

jeyhan

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<