《Java8实战》 - 读书笔记 - Stream流的基本用法
Posted 笑虾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java8实战》 - 读书笔记 - Stream流的基本用法相关的知识,希望对你有一定的参考价值。
《Java8实战》 - 读书笔记 - Stream流的基本用法
第4章 引入流
- 流是
“从支持数据处理操作的源生成的一系列元素”
。 - 流利用
内部迭代
:迭代通过filter
、map
、sorted
等操作被抽象掉了。 - 流操作有两类:
中间操作
和终端操作
。filter
和map
等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流水线,但并不会生成
任何结果
。forEach
和count
等终端操作会返回一个非流的值,并处理流水线以返回结果
。
- 流中的元素是
按需计算
的。 - 与
Stream
对应的有一套基础类型
的优化版
如:IntStream
,DoubleStream
,LongStream
与这对应的各种操作也同理。详见:底部参考资料。
第5章 使用流
表5-1 中间操作和终端操作
操作 | 类型 | 返回类型 | 使用的类型/函数式接口 | 函数描述符 |
---|---|---|---|---|
filter | 中间 | Stream<T> | Predicate<T> | T -> boolean |
distinct | 中间 (有状态-无界) | Stream<T> | ||
skip | 中间 (有状态-有界) | Stream<T> | long | |
limit | 中间 (有状态-有界) | Stream<T> | long | |
map | 中间 | Stream<R> | Function<T, R> | T -> R |
flatMap | 中间 | Stream<R> | Function<T, Stream<R>> | T -> Stream<R> |
sorted | 中间 (有状态-无界) | Stream<T> | Comparator<T> | (T, T) -> int |
anyMatch | 终端 | boolean | Predicate<T> | T -> boolean |
noneMatch | 终端 | boolean | Predicate<T> | T -> boolean |
allMatch | 终端 | boolean | Predicate<T> | T -> boolean |
findAny | 终端 | Optional<T> | ||
findFirst | 终端 | Optional<T> | ||
forEach | 终端 | void | Consumer<T> | T -> void |
collect | 终端 | R | Collector<T, A, R> | |
reduce | 终端 (有状态-有界) | Optional<T> | BinaryOperator<T> | (T, T) -> T |
count | 终端 | long |
测试数据
@Data
@AllArgsConstructor
public class Hero
private String name;
private Integer age;
private String group;
public static List<Hero> getList()
return Arrays.asList(
new Hero("张三", 23, "A"), new Hero("李四", 19, "A"), new Hero("王五", 20, "B"),
new Hero("赵六", 21, "B"), new Hero("洪七", 25, "C"), new Hero("重八", 45, "C")
);
5.1 筛选和切片
筛选 filter
Stream<T> filter(Predicate<? super T> predicate);
List<Hero> heroes = Hero.getList().stream()
.filter(hero -> hero.getAge() > 20) // 取年龄大于 20 的
.collect(Collectors.toList());
去重 distinct
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0) // 取偶数
.distinct() // 去重
.forEach(System.out::println);
截短 filter
List<Hero> heroes = Hero.getList().stream()
.filter(hero -> hero.getAge() > 20)
.limit(3) // 取前 3 个
.collect(toList());
heroes.forEach(System.out::println);
跳过 skip
先丢弃前 3 个
,在剩下
的结果中保留前 2 个
。
如果想保留后两个,可以配合排序+反转
Hero.getList().stream()
.skip(3) // 跳过前 3 个
.limit(2) // 保留前 2 个
.collect(toList())
.forEach(System.out::println);
5.2 映射
映射 map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
- 处理
流
中每一个元素
。(元素从Hero
变成了Hero
的Age
) map
还有几个针对基础类型
的优化版,直接返回基础类型流
。(免于拆箱)
函数引用写法:
Hero.getList().stream()
.map(Hero::getAge)
.forEach(System.out::println);
Lambda
写法:如果还想对年龄处理一下,可以这样
Hero.getList().stream()
.map(hero -> hero.getAge() + "岁")
.forEach(System.out::println);
躺平映射 flatMap
map
:取出每个名字,拆分成数组。相当于得到一个二维数组。如:[[张, 三], [李, 四], [王, 五]]
Arrays::stream
:接收数组创建流。flatMap
:将流中所有元素平铺开,放在同一个流里。如:[张, 三, 李, 四, 王, 五]
Hero.getList().stream()
.map(hero -> hero.getName().split(""))
.flatMap(Arrays::stream)
.forEach(System.out::println);
5.3 查找和匹配
查找 findAny, findFirst
这些方法利用了短路。不会傻傻的处理整个流。
方法 | 说明 |
---|---|
Optional<T> findFirst() | 返回第一个 元素。 |
Optional<T> findAny() | 返回任意一个 元素。 |
匹配 anyMatch, allMatch, noneMatch
方法 | 说明 |
---|---|
boolean anyMatch(Lambda) | 只要有一个 匹配就为真 。 |
boolean allMatch(Lambda) | 所有都匹配 就为真 。 |
boolean noneMatch(Lambda) | 所有都不匹配 就为真 。 |
5.4 归约
两参数版本 reduce(0, (a, c) -> a + c)
T reduce(T identity, BinaryOperator<T> accumulator);
- 参数一:初始值。
- 参数二:Lambda。
(累计值, 当前值) -> 累计值 + 当前值
Integer sum = Hero.getList().stream()
.map(Hero::getAge)
.reduce(0, (a, c) -> a + c);
System.out.println("所有人加起来 " + sum + " 岁了!");
单参数版本 reduce((a, c) -> a + c)
Optional<T> reduce(BinaryOperator<T> accumulator);
默认以流中第一
个元素
为初始值
。
Optional<Integer> sum = Hero.getList().stream()
.map(Hero::getAge)
.reduce((a, c) -> a + c);
System.out.println("所有人加起来 " + sum.orElse(999) + " 岁了!");
5.5 付诸实践
略。。。
5.6 数值流
int类型数值流 mapToInt , mapToDouble, mapToLong
Double、Long
同理
映射 | 数值流 |
---|---|
mapToInt | IntStream |
mapToDouble | DoubleStream |
mapToLong | LongStream |
转换回对象流 intStream.boxed
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
默认值 optionalInt.orElse
如果为 null
则返回 999
OptionalInt sum = Hero.getList().stream()
.mapToInt(Hero::getAge)
.reduce((a, c) -> a + c);
System.out.println("所有人加起来 " + sum.orElse(999) + " 岁了!");
生成范围值 range(开始, 结束), rangeClosed(开始, 结束)
方法 | 开始 | 结束 |
---|---|---|
public static IntStream range(int startInclusive, int endExclusive) | 包含 | 不包含 |
public static IntStream rangeClosed(int startInclusive, int endInclusive) | 包含 | 包含 |
IntStream intStream = IntStream.rangeClosed(1, 100);
intStream.forEach(System.out::println);
5.7 构建流
从值创建流 Stream.of
Stream.of("Java 8 ", "Lambdas ", "In ", "Action")
.map(String::toUpperCase)
.forEach(System.out::println);
从数组创建流 Arrays.stream
int[] numbers = 2, 3, 5, 7, 11, 13;
int sum = Arrays.stream(numbers).sum();
从文件生成流 Files.lines
java.nio.file.Files
中的很多静态方法都会返回一个流。
try(Stream<String> lines = Files.lines(Paths.get("E:\\\\test.txt"), Charset.defaultCharset()))
lines.forEach(System.out::println);
catch(IOException e)
由函数生成无限流
无限流通常配合limit
截断了来用。
迭代 Stream.iterate
接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator类型)。
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
生成 Stream.generate
与iterate方法类似,generate方法也可让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。它接受一个Supplier类型的Lambda提供新的值。
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
参考资料
java.util.stream.Stream
java.util.stream.IntStream
java.util.stream.DoubleStream
java.util.stream.LongStream
java.nio.file.Files
以上是关于《Java8实战》 - 读书笔记 - Stream流的基本用法的主要内容,如果未能解决你的问题,请参考以下文章
《Java8实战》 - 读书笔记 - Stream流操作2:用流收集数据
《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)