本文介绍java 8 Stream API提供的诸多操作,利用这些操作可以让你快速的完成复制的数据查询,如筛选、切片、映射、查找、匹配和规约。包括一些特殊流的用法:数值流、文件流、数组流,无限流。
筛选和切片
用谓词筛选
Stream接口提供了filter方法,该方法接收一个谓词作为参数,并返回一个包含所有符合谓词的元素的流。
比如,筛选出所有的素菜:
List<Dish> dishes = menu.stream().filter(Dish::isVegetarian).collect(toList());
元素去重
Stream还支持distinct方法,它会返回不重复的元素的流(根据元素的hashCode和equals方法)。
例如,筛选出下面列表中的所有不重复的偶数:
List<Integer> numbers = Arrays.asList(1,2,5,2,6,3,3,2,4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct() // 根据元素的hashcode和equals方法过滤重复元素
截断流
Stream还支持limit方法,返回一个不超过给定长度的流。所需的长度作为参数传递给limit。如果流是有序的,则最多会返回前n个元素。
比如,筛选出热量超过300卡路里的头三道菜:
List<Dish> highCaloriesTop3 = menu.stream() // 由菜单得到一个流
.filter(dish -> dish.getCalories() > 300) // 接收Lambda,从流中排除某些元素
.limit(3) // 截断流,使元素不超过指定数量
.collect(Collectors.toList()); // 将流转换为其他形式
跳过元素
Stream还支持skip方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回空流。
比如,找出超过300卡路里的菜,但忽略前2道。
List<Dish> highCalories = menu.stream()
.filter(dish -> dish.getCalories() > 300)
.skip(2)
.collect(Collectors.toList());
映射
对流中的每个元素应用函数
Stream支持map方法,接收一个函数作为参数,会对流中的每个元素应用该函数,并映射为一个新的元素。
比如,获取每道菜的名称:
List<String> dishNames = menu.stream().map(Dish::getName).collect(toList());
流的扁平化
流支持flatMap方法,可以将流中的每个值都转换为一个流,然后把所有的流连接起来形成一个流。
例1:
给定如下单词列表[‘Hello’,’World’],如何得到不重复的字符?
List<String> strings = Arrays.asList("Hello", "World");
// Arrays::stream将一个数组转换为一个流
List<String> words = strings.stream()
.map(s -> s.split("")) // 得到字符数组
.flatMap(Arrays::stream) // 将字符数组中的每个元素都换成另外一个流,然后把所有的流连接起来形成一个新的流
.distinct()
.collect(Collectors.toList());
上面如果只是使用map方法,只能得到2个字符数组,而使用flatMap就可以将数组中的每个元素映射为一个流,然后把所有的流合起来形成一个新的流。
例2:
给定2个数字列表,[1,2,3],[4,5],如何返回所有的数对?[(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
List<Integer> numList1 = Arrays.asList(1, 2, 3);
List<Integer> numList2 = Arrays.asList(4, 5);
List<int[]> nums = numList1.stream()
.flatMap(i -> numList2.stream().map(j -> new int[]{i, j}))
.collect(Collectors.toList());
查找和匹配
查找谓词是否至少匹配一个元素
使用anyMatch()方法。
比如,菜单中是否有素菜?
boolean isExistVegetarian = menu.stream().anyMatch(d -> d.isVegetarian());
检查谓词是否匹配所有元素
使用allMatch()方法。
比如,是否所有的菜卡路由都小于1000?
boolean allLess1000 = menu.stream().allMatch(d -> d.getCalories() < 1000);
检查谓词是否券都不匹配所有元素
使用noneMatch,与allMatch对应。
比如,是否是否所欲的菜卡路由都是小于1000?
boolean all