三. 使用流
流的使用一般包括三件事:
-- 一个数据源(如集合)来执行一个查询;
-- 一个中间操作链,形成一条流的流水线;
-- 一个终端操作,执行流水线,并能生成结果。
1.流操作:
筛选、切片、映射、查找、匹配、归纳。(快速完成复杂数据查询)
2.状态
无状态操作:从输入流中得到0或1个结果。他们没有内部状态。如map、filter。
有状态操作:先接受一个流再生成一个流时,需要知道先前的历史。
3.Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在。
Optional里可以迫使你显式地检查值是否存在或处理值不存在的情形的方法:
-- isPresent()将在Optional包含值的时候返回true,否则返回false;
-- ifPresent(Consumer<T> block)会在值存在的时候执行给定的代码块;
-- T get()会在值存在时返回值,否则抛出一个NoSuchElement异常;
-- T orElse(T other)会在值存在时返回值,否则返回一个默认值。
操作 | 描述 | 操作 | 类型 | 返回类型 | 使用的类型/函数式接口 | 函数描述符 | 说明 |
筛选 | 谓词筛选 | filter | 中间 | Stream<T> | Predicate<T> | T -> boolean | |
筛选各异元素 | distinct() | 中间(有状态-×××) | Stream<T> | 确保没有重复 | |||
切片 | 截短流 | limit(n) | 中间(有状态-有界) | Stream<T> | long | ||
跳过元素 | skip(n) | 中间(有状态-有界) | Stream<T> | long | skip(n)与limit(n)互补 | ||
映射 | 对流中每一个元素应用函数 | map | 中间 | Stream<R> | Function<T, R> | T -> R | 接受一个函数作为参数 |
流的扁平化:各个数组并不是分别映射成一个流,而是映射成留的内容 | flatMap(Arrays::stream) | 中间 | Stream<R> | Function<T, Stream<R>> | T -> Stream<R> | 将[h,e,l,l,o],[w,o,r,l,d]变成[h,e,l,l,o,w,o,r,l,d] | |
排序 | sorted | 中间(有状态-×××) | Stream<T> | Comparator<T> | (T, T) -> int | ||
查找 | 返回当前流中的任意元素 | findAny | 终端 | Optional<T> | //如果包含一个值就返回它,否则什么都不做 menu.stream().filter(Dish::isvegetarian).findAny().ifPresent(d -> System.out.println(d.getName)); | ||
查找第一个元素 | findFirst | 终端 | Optional<T> | ||||
匹配 | 检查谓词是否匹配至少一个元素 | anyMatch | 终端 | boolean | Predicate<T> | T -> boolean | if(menu.stream().anyMatch(Dish::isVegetarian)) { System.out.println("The menu is (somewhat) vegetarian friendly!!"); } |
检查谓词是否匹配所有元素 | allMatch | 终端 | boolean | Predicate<T> | T -> boolean | boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000); | |
没有任何元素与给定的谓词匹配 | noneMatch | 终端 | boolean | Predicate<T> | T -> boolean | boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000); | |
归约 | 对流中的数字求和 | reduce | 终端(有状态,有界) | Optional<T> | BinaryOperator<T> | (T, T) -> T | //有初始值 int sum = numbers.stream().reduce(0, Integer :: sum); //无初始值,返回Optional是考虑流中没有任何元素的情况,reduce元素无法返回其和 Optional<Integer> sum = numbers.stream().reduce((a, b) -> a * b); |
最大值 | reduce(Integer::max) | 终端(有状态,有界) | Optional<T> | Optional<Integer> max = numbers.stream().reduce(Integer::max); | |||
最小值 | reduce(Integer::min) | 终端(有状态,有界) | Optional<T> | Optional<Integer> min = numbers.stream().reduce(Integer::min); | |||
collect | 终端 | R | Collector<T, A, R> | T -> void | |||
forEach | 终端 | void | Consumer<T> | ||||
计算流中元素个数 | count | 终端 | long | long count = menu.stream().count(); |
eg:
public void testList() { List<Integer> list1 = Arrays.asList(1,2,3); List<Integer> list2 = Arrays.asList(3,4); //给定列表[1,2,3],[3,4]返回[(1,3),(1,4),(2,3),(2,4),(3,3),(3,4)] List<int[]> result = list1.stream() .flatMap(i -> list2.stream().map(j -> new int[]{i, j}) ).collect(Collectors.toList()); //给定列表[1,2,3],[3,4]返回总和能被3整除的数对:(2,4)和(3,3) List<int[]> result1 = list1.stream() .flatMap(i -> list2.stream().filter(j -> (i + j) % 3 == 0) .map(j -> new int[]{i, j}) ) .collect(Collectors.toList()); }
4.特殊的流
数值流
原始类型流特化:特化是为了避免装箱造成的复杂性(类似int和Integer之间的效率差异)。
原始类型特化流接口 | 映射到数值流 | 将数值流转换回对象流 | Optional原始类型特化 |
IntStream | mapToInt | boxed() | OptionalInt |
DoubleStream | mapToDouble | boxed() | OptionalDouble |
LongStream | mapToLong | boxed() | OptionalLong |
数值范围:IntStream和LongStream有静态方法帮助生成这种范围:range(接受起始值)和rangeClosed(接受结束值)。
5.构建流
创建流的方法 | 说明 | 举例 |
Stream.of | 通过显式值创建流 | Stream.of("hello", "world") |
Stream.empty | 得到空流 | Stream.empty() |
Arrays.stream | 由数组创建流 | int[] numbers = {2,3,4,5}; int sum = Arrays.stream(numbers).sum(); |
Files.lines | 由文件生成流,其中每个元素都是给定文件中的一行 | long uniqueWords = 0; try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){ uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) //生成单词流 .distinct().count;//删除重复项并得出共有多少各不相同的单词 } catch(IOException e) {} |
Stream.iterate | 由函数生成流:创建无限流,使用limit()方法限制流的大小 | //依次对每个新生成的值应用函数 Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println); |
Stream.generate | //接受一个Supplier<T>类型的Lambda提供新的值 IntStream tows = IntStream.generate(new IntSupplier() { public int getAsInt() { return 2; } }); |