流是Java API的新成员,它允许你以声明性方式处理数据集合
流操作有两个重要的特点:
- 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
2.内部迭代——与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。
流与集合的区别:
集合中的每个元素都得先算出来才能添加到集合中
流就像是一个延迟创建的集合,只有在消费者要求的时候才会计算值
流的构建
Stream.of("Java 8 ", "Lambdas ", "In ", "Action") 由值创建流
Arrays.stream(numbers) 由数组创建流
List.stream() 集合创建流
Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate。 这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。由iterate 和generate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说, 应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值。
流的使用
可以使用流进行筛选&切片、映射、查找&匹配、归约。
下列使用均以此List为例
List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH));
-
筛选&切片
filter:筛选
// 筛选出菜单中所有素食,filter接收一个函数式接口,我们可以使用之前提到的方法引用 // collect是一个规约操作,之后介绍 menu.stream().filter(Dish::isVegetarian).collect(Collectors.toList())
distinct:去重
// 使用流去除集合中的重复元素,并生成新的集合 List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5); list.stream().distinct().collect(Collectors.toList());
limit:截断
// 取集合中前3个元素,并生成新的集合 List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5); list.stream().limit(3).collect(Collectors.toList());
skip:跳过
// 跳过前4个元素,并生成新的集合 List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5); list.stream().skip(4).collect(Collectors.toList());
-
映射
map:接受一个函数作为参数,并将其映射成一个新的元素
// 给定一个Integer的集合,使用map映射为由每个数的平方构成的String集合 List<Integer> list = Arrays.asList(1,2,3,4,5); List<String> list2= list.stream().map((Integer i)->i*i+"").collect(Collectors.toList());
flatMap :将流合并为单个流
// 给定两个数字列表,返回所有的数对 结果1,3 1,4 2,3 2,4 3,3 3,4 // 例中使用了两个流,用flatMap合并为了单个流 List<Integer> list1 = Arrays.asList(1,2,3); List<Integer> list2 = Arrays.asList(3,4); List<int[]> list3 = list1.stream() .flatMap((Integer i)->list2.stream() .map((Integer j)->new int[]{i,j})) .collect(Collectors.toList());
-
查找和匹配
anyMatch:流中是否有一个元素能匹配给定的谓词
// 若集合中存在任一元素等于8,返回true,否则返回false List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5); Boolean flag = list.stream().anyMatch((Integer a)->a==8);
allMatch:检查谓词是否匹配所有元素
noneMatch:它可以确保流中没有任何元素与给定的谓词匹配
findAny:方法将返回当前流中的任意元素
// 返回任一元素,结果为Optional<T>类型的值,防止null错误 List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5); Integer i = list.stream().findAny();
findFirst:查找第一个元素。
<u>找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,请使用findAny,因为它在使用并行流 时限制较少。</u>
-
归约
reduce:用于求和、最大最小值等规约操作。
reduce接受两个参数: 一个初始值,这里是0; 一个BinaryOperator<T>来将两个元素结合起来产生一个新值
// 求和 List<Integer> numbers = Arrays.asList(1,2,2,3,4,5,5,5); int sum = numbers.stream().reduce(0, (a, b) -> a + b); // 或者使用已有的sum、min、max方法用来求和、获取最大、最小值 。例: numbers.stream().reduce(Integer::min);
-
数值流
stream.mapToInt() 可以将普通流转为数值流
intStream.boxed() 将数值流转为普通流
<u>数值流使用range和rangeClosed方法可以生成指定范围内的流。这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但 range是不包含结束值的。</u>
流收集器
我们在之前提到了collect,其作用是规约,将流中的元素使用Collector累积成一个汇总结果。
Collectors.counting()汇总数量
Collectors.maxBy() 获取最大值
例:
// 获取菜单列表中卡路里数值最大的菜。
Comparator<Dish> dishCaloriesComparator=Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));
Collectors.summingLong和Collectors.summingDouble 这两个方法用于求和,可以用于求和字段为long或double的情况。
Collectors.averagingInt(),连同对应的averagingLong和 averagingDouble 用于计算数值的平均数
万能方法:Collectors.summarizing(),可以获取元素个数,总和,平均值、最大值、最小值。
Collectors.joining() 内部使用了StringBuilder来把生成的字符串逐个追加起来.
Collectors.reducing() 接受一个BinaryOperator<t>参数,也就是一个 BiFunction<T,T,T>。这就意味着它需要的函数必须能接受两个参数,然后返回一个相同类型的值。
Collectors.groupingBy() 分组
// 根据卡路里进行分组,返回一个Map<CaloricLevel, List<Dish>> 类型的结果
public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel
= menu.stream().collect( groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
}));
// 多级分组,根据类型分组,再统计数量,返回结果Map: {MEAT=3, FISH=2, OTHER=4}
Map<Dish.Type,Map<CaloricLevel,List<Dish>>> map = menu.stream()
.collect(Collectors.groupingBy(Dish::getType,Collectors.groupingBy((Dish d)->{
if (d.getCalories()<400){return CaloricLevel.DIET;}
else {return CaloricLevel.FAT;}
})));
/**
普通的单参数groupingBy(f)(其中f是分类函数)实际上是groupingBy(f, toList())的简便写法。
*/
Collectors.partitioningBy() 分区
// 区分出素食与非素食
menu.stream().collect(partitioningBy(Dish::isVegetarian))
并行流
<u>并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。这样一来,你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核。</u>
Stream.parallel() 顺序流转为并行流
Stream.sequential () 并行流转为顺序流
在使用并行流时,要避免共享可变状态,确保并行Stream得到正确的结果。
并行流内部使用了默认的ForkJoinPool ,它默认的 线程数量就是你的处理器数量 ,可 以 通 过 系 统 属 性 java.util.concurrent.ForkJoinPool.common. parallelism来改变线程池大小 ,System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");