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

浙公网安备 33010602011771号