1 筛选和切片
该功能的使用场景主要是用来筛选流中的元素。
1.1 用谓词进行筛选
谓词:一个返回boolean的函数
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
1.2 筛选各异的元素
List<Integer> numbers = Arrays.asList(1,, 2, 1, 3, 2, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
1.3 截断流
流支持limit(n)方法,该方法会返回一个不超过给定长度的流。例如下面的方法就是获取Calory大于300的菜谱。
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
1.4 跳过元素
流还支持skip(n)方法,返回一个扔掉了前n个元素的流。
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
2 映射
将流中的元素变成其他的值类型
2.1 对流中的每个元素应用函数
流支持map方法,该方法会接受一个函数作为参数,并将其应用到流中的每个元素上,然后将其映射为一个新的元素。
例如如下的代码段,给定一个单词列表,返回一个显示每个单词中含有几个字母。
List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
2.2 流的扁平化
针对一张单词列表,如何返回一张列表,列出里面各不相同的字符呢?例如,给定单词列表["Hello","World"],想要返回["H","e","l","l","o","W","o","r","l","d"]。最初的想法,可能使用如下的代码。
String[] words = {"Hello", "World"};
words.stream()
.map(word -> word.split(""))
.distinct()
.collect(Collectors.toList());
但这种方式的问题在于,传递给map方法的Lambada为每个单词返回了一个String[]。因此,map返回的流实际上是Stream<String[]>类型。下图表述了上述代码存在的问题。
可以使用flatMap方法来解决该问题。具体代码如下
List<String> uniqueCharacters = words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
在增加flatMap后,返回的流就是Stream<String>类型。具体可以参考下图。
测验1: 给定一个数字列表,例如[1,2,3,4],返回每个数的平方构成的列表。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = numbers
.stream()
.map(i -> i * i)
.collect(Collectors.toList());
测验2:给定两个数字表,返回所有的数对。例如,给定列表[1, 2, 3]和列表[3, 4],应 该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> result = numbers1
.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.collect(Collectors.toList());
测验3: 扩展前一个例子,只返回总和能被3整除的数对
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> result = numbers1
.stream()
.flatMap(i -> numbers2.stream().filter(j -> (i + j) % 3 == 0).map(j -> new int[]{i, j}))
.collect(Collectors.toList());
3 用流收集数据
3.1 规约
使用流来实现数组求和。注意,下面这种方式在日常工作中是比较少用的,可以直接使用Stream的sum()方法。
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
Integer reduce = numbers1.stream().reduce(0, (a, b) -> a + b);
Integer reduce = numbers1.stream().reduce(0, Integer::sum);
获取菜单中热量最高的菜
List<Dish> menu = new ArrayList<>();
Optional<Dish> maxCalories = menu.stream()
.collect(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)));
Optional<Dish> maxCalories = menu.stream()
.max(Comparator.comparingInt(Dish::getCalories));
3.2 汇总
获取菜单中热量总和
List<Dish> menu = new ArrayList<>();
Integer sum = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
获取菜单热量的平均值
List<Dish> menu = new ArrayList<>();
Double avg = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));
一站式的解决方式
List<Dish> menu = new ArrayList<>();
IntSummaryStatistics summaryStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
summaryStatistics.getSum();
summaryStatistics.getAverage();
summaryStatistics.getCount();
summaryStatistics.getMax();
summaryStatistics.getMin();
3.3 分组
根据菜单类型分组
Map<Dish.Type, List<Dish>> dishesByType =
menu.stream().collect(Collectors.groupingBy(Dish::getType));
稍微复杂点的分组
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
Collectors.groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return
CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
}));