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

第四章 引入流

1、什么是流

流是Java API的新成员,它容许你以声明性方式处理数据集合(经过查询语句来表达,而不是临时编写一个实现)。就如今来讲,你能够把它们当作遍历数据集的高级迭代器。此外,流还能够透明地并行处理,你无需写任何多线程代码。

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

以前(Java 7):程序员

List<Dish> lowCaloricDishes = new ArrayList<>(); 
for(Dish d: menu){ 
 if(d.getCalories() < 400){ 
 lowCaloricDishes.add(d); 
 } 
} 
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
 public int compare(Dish d1, Dish d2){ 
 return Integer.compare(d1.getCalories(), d2.getCalories()); 
 } 
}); 
List<String> 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<String> lowCaloricDishesName = 
 menu.stream() 
 .filter(d -> d.getCalories() < 400) 
 .sorted(comparing(Dish::getCalories))
 .map(Dish::getName) 
 .collect(toList());

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

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

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

Map<Dish.Type, List<Dish>> 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<String> threeHighCaloricDishNames = 
 menu.stream() 
 .filter(d -> d.getCalories() > 300)
 .map(Dish::getName)
 .limit(3)         //筛选(filter)、提取(map)或截断(limit)
 .collect(toList()); 
System.out.println(threeHighCaloricDishNames); 
//在调用collect以前,没有任何结果产生,实际上根本就没有从menu里选择元素

2、流与集合

图片描述

  • 和迭代器相似,流只能遍历一次。遍历完以后,咱们就说这个流已经被消费掉了。
  • 使用Collection接口须要用户去作迭代(好比用for-each),这称为外部迭代。 相反,Streams库使用内部迭代——它帮你把迭代作了,还把获得的流值存在了某个地方,你只要给出一个函数说要干什么就能够了。

图片描述

3、流操做

java.util.stream.Stream中的Stream接口定义了许多操做。它们能够分为两大类:app

  • 中间操做
    能够链接起来的流操做称为中间操做,诸如filter或sorted等

图片描述

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

图片描述

eg:(注意循环合并)编程语言

List<String> 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是两个独立的操做,但它们合并到同一次遍历中了(咱们把这种技术叫做循环合并)。

4、使用流

流的使用通常包括三件事:

  • 一个数据源(如集合)来执行一个查询;
  • 一个中间操做链,造成一条流的流水线;
  • 一个终端操做,执行流水线,并能生成结果。
相关文章
相关标签/搜索