# Java Stream流
# 参阅
Java 8 中的 Streams API 详解 (opens new window) Java 8中Stream API的这些奇技淫巧! (opens new window)
# 使用流
# 构造流的方式
- 构造流的基本方式(如何获得流)
// 通过Collection的stream方法构造
Stream<?> stream = list.stream();
// Arrays.stream构造各种类型的数组流
Stream<String> stream = Arrays.stream(new String[]{"a", "b", "c"});
// 通过Stream.of创建任意数量的流
Stream<String> stringStream = Stream.of("a", "b");
- 构造数值流 使用IntStream和LongStream的静态方法构造数值流,range不包含结束值。
// 筛选出1-100中的偶数
int count = IntStream.rangeClosed(1,100).filter(n->n%2==0).count;
流转换为其他数据类型(如何从流转换成需要的数据类型)
由文件生成流 java.nio.file.Files提供了很多返回流的静态方法
由函数生成流 Stream.iterate和Stream.generate可以创建无限流
// Stream.iterate
Stream.iterate(0, n->n+2).limit(10).foreach(System.out::println);
// Stream.generate
Stream.generate(Math.random).limit(5).foreach(System.out::println);
// 生成全是1的无限流
Stream.generate(()->1).limit(5).foreach(System.out::println);
# 操作流的方法
流的三类操作方式
Intermediate
Terminal
Short-circuiting
映射
| 原生 | 衍生 | 入参->返回 |
|---|---|---|
| map | mapToInt、mapToLong、mapToDouble | Function<T,R> -> Stream[R] |
| flatmap | flatmapToInt、flatmapToLong、flatmapToDouble | Function<T,Stream[R]> -> Stream[R] |
衍生操作的作用:
重点解释flatmap使用:
map的映射关系是1:1的, 但是存在有层级结构的Stream, 需要使用flatM ap使其扁平化(将最底层元素抽出来放到一起)
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(2, 3),
Arrays.asList(4, 5, 6)
);
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
最终outputStream中没有List了.
筛选
| 谓词 | 作用 | 入参->返回 |
|---|---|---|
| filter | 过滤掉false | Predicate[T] -> Stream[T] |
| distinct | 根据hashCode和equals方法去重 | () -> Stream[T] |
| skip | 扔掉前n个元素 | long -> Stream[T] |
| limit | 返回前n个元素 | long -> Stream[T] |
| peek | ||
| foreach | 循环 | Consumer[T] -> void |
| sorted | 排序 | Comparator[T] -> Stream[T] |
匹配和查找
| 操作 | 作用 | 入参->返回 |
|---|---|---|
| anyMatch | 至少匹配一个元素 | Predicate[T] -> boolean |
| allMatch | 是否匹配所有元素 | Predicate[T] -> boolean |
| noneMatch | 没有任何元素匹配 | Predicate[T] -> boolean |
| findAny | 返回当前流中任意元素 | () -> Optional[?] |
| findFirst | 查找第一个元素 | () -> Optional[?] |
Match操作都是返回boolean的结果,并且都使用了短路操作,即当anyMatch有一个匹配了就直接返回true,而不再继续匹配了。
findAny和findFirst也都是短路操作,两者在串行使用时效果一致,当并行时请尽量使用findAny,因为其并行限制少(性能好)。
归约
计数、求和都是归约的特殊场景应用。
| 操作 | 作用 | 入参->返回 |
|---|---|---|
| reduce | 归约 | BinaryOperator[T] -> Optional[T] |
| max | 求最大值 | () -> Optional[T] |
| min | 求最小值 | () -> Optional[T] |
| count | 计数 | () -> long |
收集器(Collectors)
collect、Collectors和Collector的区别:
- collect是Stream提供的收集操作
- Collectors是工具类,提供了很多静态方法来返回Collector
- Collector作为collect方法的参数,用于构造结果集
Collectors类提供的操作:
| 操作 | 作用 | 入参->返回 |
|---|---|---|
| toList | ||
| toSet | ||
| toMap | ||
| toConcurrentMap | ||
| groupingBy | 分组 | Function<? super T, ? extends K> -> Collector |
| minBy | 求最小值 | Comparator<? super T> -> Collector |
| maxBy | 求最大值 | Comparator<? super T> -> Collector |
| counting | ||
| joining | 连接字符串(通过toString) | |
| mapping | ||
| summingInt/Long/Double | 汇总/求和 | ToIntFunction<? super T> |
| averagingInt/Long/Double | 计算平均数 | ToIntFunction<? super T> |
| summarizingInt/Long/Double | 总结(求和/平均数/最大最小值) | ToIntFunction<? super T> |
| reducing | 归约 | |
| partitioningBy | 分区 |
案例:
计数
> Map<String, Long> map = fruitList.stream().map(Fruit::getName).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
排序
> map.entrySet().stream().sorted(Map.Entry.<String, Long>comparingByValue().reversed()).forEachOrdered(System.out::println);
累加求和
> Map<String, Integer> sumMap = fruitList.stream().collect.(Collectors.groupingBy(Fruit::getName, Collectors.summingInt(Fruit::getPrice)));
分组
> Map<String, List<Fruit>> groupMap = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName));
Collectors.mapping
//group by price, uses 'mapping' to convert List<Fruit> to List<String>
Map<String, List<Integer>> groupMap = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.mapping(Fruit::getPrice, Collectors.toList())));
Collectors.toMap
Stream().count()和collect(Collectors.counting())的区别:
自定义收集器
比较器
Comparator类提供了静态方法来支持
# 数值流
由于基本类型的包装类存在装箱拆箱操作,数据量大会非常影响性能,因此Java 8提供了数值流。
映射为数值流
- mapToInt
- mapToDouble
- mapToLong
案例:
// mapToInt返回IntStream
int ages = list.stream().mapToInt(User::getAge).sum();
转换为对象流
IntStream上的lambda操作只能使用int类型,因此有时需要将其转换为Integer。
案例:
IntStream intStream = list.stream().mapToInt(User::getAge);
Stream<Integer> stream = intStream.boxed();
Stream不会返回为null的集合
# Optianl
数值类型的Optional 当遇到求和的场景,如何区分没有值和为0的场景呢?因此Optional提供了数值类型。
- OptionalInt
- OptionalDouble
- OptionalLong
案例:
OptionlInt maxAge = users.stream().mapToInt(User::getAge).max();
// 如果没有最大值可以显示提供
int max = maxAge.orElse(1);
# 流中用到的函数
# 流的实现原理
← Java8 Java中创建对象有哪几种方式 →