Java8实战-总结18
- 使用流
- 筛选和切片
- 用谓词筛选
- 筛选各异的元素
- 截短流
- 跳过元素
使用流
流让你从外部迭代转向内部迭代。这样,就用不着写下面这样的代码来显式地管理数据集合的迭代(外部迭代)了:
List<Dish> vegetarianDishes = new ArrayList<>();
for(Dish d : menu) {
if(d.isVegetarian()) {
vegetarianDishes.add(d);
}
}
可以使用支持filter
和collect
操作的Stream API
(内部迭代)管理对集合数据的迭代。只需要将筛选行为作为参数传递给filter
方法就行了。
import static java.util.stream.Collectors.toList;
List<Dish> vegetarianDishes =
menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
这种处理数据的方式很有用,因为你让Stream API
管理如何处理数据。这样Stream API
就可以在背后进行多种优化。此外,使用内部迭代的话,Stream API
可以决定并行运行你的代码。这要是用外部迭代的话就办不到了,因为你只能用单一线程挨个迭代。
下面会介绍Stream API
支持的许多操作。这些操作能让你快速完成复杂的数据查询,如筛选、切片、映射、查找、匹配和归约。接下来,会看看一些特殊的流:数值流、来自文件和数组等多种来源的流,最后是无限流。
筛选和切片
来看看如何选择流中的元素:用谓词筛选,筛选出各不相同的元素,忽略流中的头几个元素,或将流截短至指定长度。
用谓词筛选
Streams
接口支持filter
方法。该操作会接受一个谓词(一个返回boolean
的函数)作为参数,并返回一个包括所有符合谓词的元素的流。例如,可以像下图所示的这样,筛选出所有素菜,创建一张素食菜单:
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)//方法引用检查菜肴是否适合素食者
.collect(toList());
筛选各异的元素
流还支持一个叫作distinct
的方法,它会返回一个元素各异(根据流所生成元素的hashCode
和equals
方法实现)的流。例如,以下代码会筛选出列表中所有的偶数,并确保没有重复。
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
截短流
流支持limit(n)
方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit
。如果流是有序的,则最多会返回前n
个元素。比如,你可以建立一个List
,选出热量超过300卡路里的头三道菜:
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
下图展示了filter
和limit
的组合。可以看到,该方法只选出了符合谓词的头三个元素,然后就立即返回了结果。
请注意limit
也可以用在无序流上,比如源是一个set
。这种情况下,limit
的结果不会以任何顺序排列。
跳过元素
流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。请注意,limit(n)和skip(n)是互补的!例如,下面的代码将跳过超过300卡路里的头两道菜,并返回剩下的。图5-4展示了这个查询。
List<Dish> dishes = menu.stream()
.filter(d -> d.getcalories()> 300)
.skip(2)
.collect(toList ());
测验:筛选
你将如何利用流来筛选前两个荤菜呢?
答案:可以把filter和limit复合在一起来解决这个问题,并用collect(toList())将流转换成一个列表。
List<Dish> dishes =menu.stream()
.filter(d -> d.getType()== Dish.Type.MEAT)
.limit(2)
.collect(toList();