java8-stream流的概念、与集合有何异同

流是Java API 的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。

就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理。

java7处理集合:

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

  用了一个“垃圾变量”lowCaloricDishes。它唯一的作用就是作为一次性的中间容器。在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());

  代码是以声明性方式写的

  你可以把几个基础操作链接起来,来表达复杂的数据处理流水线

流的定义:从支持数据处理操作的源生成的元素序列

Java 现有的集合概念和新的流概念都提供了接口,来配合代表元素型有序值的数据接口。所谓有序,就是说我们一般是按顺序取值,而不是随机取用。

和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样。

外部迭代与内部迭代

使用Collection 接口需要用户去做迭代(比如用for-each),这称为外部迭代。

相反,Stream库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方。

  用for-each 循环外部迭代

List<String> names = new ArrayList<>();
  for(Dish dish: menu){
    names.add(dish.getName());
}

  集合:用背后的迭代器做显式外部迭代

List<String> names = new ArrayList<>();
Iterator<String> iterator = menu.iterator();
  while(iterator.hasNext()) {
    Dish dish = iterator.next();
    names.add(dish.getName());
}

  流:内部迭代

List<String> names = menu.stream()
  .map(Dish::getName)
  .collect(toList());

  Streams 库的内部迭代可以自动选择一种适合你硬件的数据表示和并行实现

流操作:可以连接起来的流操作称为中间操作,关闭流的操作称为终端操作

中间操作:

诸如filter 或sorted 等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。

重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。

这是因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。

List<String> names =
menu.stream()
.filter(dish -> dish.getCalories() > 300)
.map(dish -> dish.getName())
.limit(3)
.collect(toList());

  终端操作:

终端操作会从流的流水线生成结果,其结果是任何不是流的值,比如List、Integer,甚至void。

forEach 是一个返回void 的终端操作:

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

  

总而言之,流的使用一般包括三件事:
 一个数据源(如集合)来执行一个查询;
 一个中间操作链,形成一条流的流水线;
 一个终端操作,执行流水线,并能生成结果。

中间操作:

操 作    类 型      返回类型       操作参数       函数描述符
filter   中间     Stream<T>     Predicate<T>     T -> boolean
map     中间     Stream<R>     Function<T, R>   T -> R
limit     中间     Stream<T>
sorted   中间     Stream<T>     Comparator<T>    (T, T) -> int
distinct  中间      Stream<T> 

  终端操作

操 作     类 型    返回类型       目 的
forEach   终端     void     消费流中的每个元素并对其应用Lambda
count     终端     long     返回流中元素的个数
collect   终端     (generic)   把流归约成一个集合,比如List、Map,甚至是Integer

  

关于流的更多操作比如过滤、切片、查找、匹配、映射和归约,它们可以用来表达复杂的数据处理查询,在下一节展开说明。

 

posted @ 2021-07-05 23:58  如果可以就好了  阅读(367)  评论(0)    收藏  举报