提交 9f57fbac 编写于 作者: W wizardforcel

2021-05-25 21:24:23

上级 5287bfe7
...@@ -45,7 +45,7 @@ public class Melon { ...@@ -45,7 +45,7 @@ public class Melon {
假设我们有一个客户——我们叫他马克——他想开一家卖瓜的公司。我们根据他的描述塑造了前面的班级。他的主要目标是拥有一个库存应用来支持他的想法和决策,因此需要创建一个必须基于业务需求和发展的应用。我们将在下面几节中查看每天开发此应用所需的时间。 假设我们有一个客户——我们叫他马克——他想开一家卖瓜的公司。我们根据他的描述塑造了前面的班级。他的主要目标是拥有一个库存应用来支持他的想法和决策,因此需要创建一个必须基于业务需求和发展的应用。我们将在下面几节中查看每天开发此应用所需的时间。
# 第 1 天(按瓜过滤) # 第 1 天(按瓜的类型过滤)
有一天,马克让我们提供一个功能,可以按瓜的类型过滤瓜。因此,我们创建了一个名为`Filters`的实用程序类,并实现了一个`static`方法,该方法将瓜列表和要过滤的类型作为参数。 有一天,马克让我们提供一个功能,可以按瓜的类型过滤瓜。因此,我们创建了一个名为`Filters`的实用程序类,并实现了一个`static`方法,该方法将瓜列表和要过滤的类型作为参数。
...@@ -95,7 +95,7 @@ public static List<Melon> filterByWeight( ...@@ -95,7 +95,7 @@ public static List<Melon> filterByWeight(
这与`filterByType()`类似,只是它有不同的条件/过滤器。作为开发人员,我们开始明白,如果我们继续这样做,`Filters`类最终会有很多方法,这些方法只是重复代码并使用不同的条件。我们非常接近一个*样板代码*案例。 这与`filterByType()`类似,只是它有不同的条件/过滤器。作为开发人员,我们开始明白,如果我们继续这样做,`Filters`类最终会有很多方法,这些方法只是重复代码并使用不同的条件。我们非常接近一个*样板代码*案例。
# 第 3 天(按类型和重量过滤西瓜) # 第 3 天(按类型和重量过滤瓜)
事情变得更糟了。马克现在要求我们添加一个新的过滤器,按类型和重量过滤西瓜,他需要这个很快。然而,最快的实现是最丑陋的。过来看: 事情变得更糟了。马克现在要求我们添加一个新的过滤器,按类型和重量过滤西瓜,他需要这个很快。然而,最快的实现是最丑陋的。过来看:
...@@ -193,7 +193,7 @@ List<Melon> huge = Filters.filterMelons( ...@@ -193,7 +193,7 @@ List<Melon> huge = Filters.filterMelons(
melons, new HugeMelonPredicate()); melons, new HugeMelonPredicate());
``` ```
# 第 5 天(实另外 100 个过滤器) # 第 5 天(实另外 100 个过滤器)
马克要求我们再安装 100 个过滤器。这一次,我们有足够的灵活性和支持来完成这项任务,但是我们仍然需要为每个选择标准编写 100 个实现`MelonPredicate`的策略或类。此外,我们必须创建这些策略的实例,并将它们传递给`filterMelons()`方法。 马克要求我们再安装 100 个过滤器。这一次,我们有足够的灵活性和支持来完成这项任务,但是我们仍然需要为每个选择标准编写 100 个实现`MelonPredicate`的策略或类。此外,我们必须创建这些策略的实例,并将它们传递给`filterMelons()`方法。
...@@ -300,7 +300,7 @@ Predicate<Melon> predicate = (Melon m) ...@@ -300,7 +300,7 @@ Predicate<Melon> predicate = (Melon m)
-> "Watermelon".equalsIgnoreCase(m.getType()); -> "Watermelon".equalsIgnoreCase(m.getType());
``` ```
# 167 简而言之就是兰博达斯 # 167 Lambda 简述
剖析 Lambda 表达式将显示三个主要部分,如下图所示: 剖析 Lambda 表达式将显示三个主要部分,如下图所示:
...@@ -329,7 +329,7 @@ FilenameFilter filter = new FilenameFilter() { ...@@ -329,7 +329,7 @@ FilenameFilter filter = new FilenameFilter() {
Lambda 支持行为参数化,这是一个很大的优点(查看前面的问题以获得对此的详细解释)。最后,请记住 Lambda 只能在函数式接口的上下文中使用。 Lambda 支持行为参数化,这是一个很大的优点(查看前面的问题以获得对此的详细解释)。最后,请记住 Lambda 只能在函数式接口的上下文中使用。
# 168.实现环绕执行模式 # 168 实现环绕执行模式
环绕执行模式试图消除围绕特定任务的*样板*代码。例如,为了打开和关闭文件,特定于文件的任务需要被代码包围。 环绕执行模式试图消除围绕特定任务的*样板*代码。例如,为了打开和关闭文件,特定于文件的任务需要被代码包围。
......
# 函数式编程-深入研究 # 函数式编程——深入研究
本章包括 22 个涉及 Java 函数式编程的问题。这里,我们将重点讨论在流中遇到的涉及经典操作的几个问题(例如,`filter``map`),并讨论无限流、空安全流和缺省方法。这个问题的综合列表将涵盖分组、分区和收集器,包括 JDK12`teeing()`收集器和编写自定义收集器。此外,还将讨论`takeWhile()``dropWhile()`、组合函数、谓词和比较器、Lambda 测试和调试以及其他一些很酷的话题。 本章包括 22 个涉及 Java 函数式编程的问题。这里,我们将重点讨论在流中遇到的涉及经典操作的几个问题(例如,`filter``map`),并讨论无限流、空安全流和缺省方法。这个问题的综合列表将涵盖分组、分区和收集器,包括 JDK12`teeing()`收集器和编写自定义收集器。此外,还将讨论`takeWhile()``dropWhile()`、组合函数、谓词和比较器、Lambda 测试和调试以及其他一些很酷的话题。
...@@ -121,7 +121,7 @@ public void testReduceStrings() throws Exception { ...@@ -121,7 +121,7 @@ public void testReduceStrings() throws Exception {
} }
``` ```
# 178 使用 Lambda 的测试方法 # 178 测试使用 Lambda 的方法
让我们从测试一个没有包装在方法中的 Lambda 开始。例如,以下 Lambda 与一个字段关联(用于重用),我们要测试其逻辑: 让我们从测试一个没有包装在方法中的 Lambda 开始。例如,以下 Lambda 与一个字段关联(用于重用),我们要测试其逻辑:
...@@ -422,7 +422,7 @@ Stream<Integer> unorderedStream = list.stream() ...@@ -422,7 +422,7 @@ Stream<Integer> unorderedStream = list.stream()
.unordered(); .unordered();
``` ```
# 无限序流 # 无限序流
通过`Stream.iterate​(T seed, UnaryOperator<T> f)`可以得到无限的有序流。结果流从指定的种子开始,并通过将`f`函数应用于前一个元素(例如,`n`元素是`f(n-1)`来继续)。 通过`Stream.iterate​(T seed, UnaryOperator<T> f)`可以得到无限的有序流。结果流从指定的种子开始,并通过将`f`函数应用于前一个元素(例如,`n`元素是`f(n-1)`来继续)。
...@@ -558,7 +558,7 @@ AIW?F1obl3KPKMItqY8> ...@@ -558,7 +558,7 @@ AIW?F1obl3KPKMItqY8>
`Stream.ints()` comes with two more flavors: one that doesn't take any argument (an unlimited stream of integers) and another that takes a single argument representing the number of values that should be generated, that is, `ints​(long streamSize)`. `Stream.ints()` comes with two more flavors: one that doesn't take any argument (an unlimited stream of integers) and another that takes a single argument representing the number of values that should be generated, that is, `ints​(long streamSize)`.
# 无限序贯无序流 # 无限连续无序流
为了创建一个无限连续的无序流,我们可以依赖于`Stream.generate​(Supplier<? extends T> s)`。在这种情况下,每个元素由提供的`Supplier`生成。这适用于生成恒定流、随机元素流等。 为了创建一个无限连续的无序流,我们可以依赖于`Stream.generate​(Supplier<? extends T> s)`。在这种情况下,每个元素由提供的`Supplier`生成。这适用于生成恒定流、随机元素流等。
...@@ -596,7 +596,7 @@ List<String> result = passwordStream ...@@ -596,7 +596,7 @@ List<String> result = passwordStream
213c1b1c, 2badc$21, d33321d$, @a0dc323, 3!1aa!dc, 0a3##@3!, $!b2#1d@, 0@0#dd$#, cb$12d2@, d2@@cc@d 213c1b1c, 2badc$21, d33321d$, @a0dc323, 3!1aa!dc, 0a3##@3!, $!b2#1d@, 0@0#dd$#, cb$12d2@, d2@@cc@d
``` ```
# 谓词返回`true`时执行 # 谓词返回`true`时执行
从 JDK9 开始,添加到`Stream`类中最有用的方法之一是`takeWhile​(Predicate<? super T> predicate)`。此方法具有两种不同的行为,如下所示: 从 JDK9 开始,添加到`Stream`类中最有用的方法之一是`takeWhile​(Predicate<? super T> predicate)`。此方法具有两种不同的行为,如下所示:
...@@ -768,7 +768,7 @@ List<Integer> result = setOfInts.stream() ...@@ -768,7 +768,7 @@ List<Integer> result = setOfInts.stream()
映射一个流的元素是一个中间操作,用于将这些元素转换成一个新的版本,方法是将给定的函数应用于每个元素,并将结果累加到一个新的`Stream`(例如,将`Stream<String>`转换成`Stream<Integer>`,或将`Stream<String>`转换成另一个`Stream<String>`等)。 映射一个流的元素是一个中间操作,用于将这些元素转换成一个新的版本,方法是将给定的函数应用于每个元素,并将结果累加到一个新的`Stream`(例如,将`Stream<String>`转换成`Stream<Integer>`,或将`Stream<String>`转换成另一个`Stream<String>`等)。
# 使用流.map() # 使用`Stream.map()`
基本上,我们调用`Stream.map​(Function<? super T,​? extends R> mapper)`对流的每个元素应用`mapper`函数。结果是一个新的`Stream`。不修改源`Stream` 基本上,我们调用`Stream.map​(Function<? super T,​? extends R> mapper)`对流的每个元素应用`mapper`函数。结果是一个新的`Stream`。不修改源`Stream`
...@@ -873,7 +873,7 @@ Gac(1500g), Hemi(1100g), Gac(2500g), Apollo(1500g), Horned(1200g) ...@@ -873,7 +873,7 @@ Gac(1500g), Hemi(1100g), Gac(2500g), Apollo(1500g), Horned(1200g)
根据经验,在使用`peek()`改变状态之前,要三思而后行。另外,请注意,这种做法是一种辩论,属于不良做法,甚至反模式的保护伞。 根据经验,在使用`peek()`改变状态之前,要三思而后行。另外,请注意,这种做法是一种辩论,属于不良做法,甚至反模式的保护伞。
# 使用流.flatMap() # 使用`Stream.flatMap()`
正如我们刚才看到的,`map()`知道如何在`Stream`中包装一系列元素。 正如我们刚才看到的,`map()`知道如何在`Stream`中包装一系列元素。
...@@ -1000,7 +1000,7 @@ List<String> melons = Arrays.asList( ...@@ -1000,7 +1000,7 @@ List<String> melons = Arrays.asList(
"Hemi", "Cantaloupe", "Horned", "Hemi", "Hemi"); "Hemi", "Cantaloupe", "Horned", "Hemi", "Hemi");
``` ```
# 芬丹 # `findAny()`
`findAny()`方法从流中返回任意(不确定)元素。例如,以下代码片段将返回前面列表中的元素: `findAny()`方法从流中返回任意(不确定)元素。例如,以下代码片段将返回前面列表中的元素:
...@@ -1028,7 +1028,7 @@ String anyApollo = melons.stream() ...@@ -1028,7 +1028,7 @@ String anyApollo = melons.stream()
这一次,结果将是`nope`。列表中没有`Apollo`,因此`filter()`操作将产生一个空流。此外,`findAny()`还将返回一个空流,因此`orElse()`将返回最终结果作为指定的字符串`nope` 这一次,结果将是`nope`。列表中没有`Apollo`,因此`filter()`操作将产生一个空流。此外,`findAny()`还将返回一个空流,因此`orElse()`将返回最终结果作为指定的字符串`nope`
# 首先查找 # `findFirst()`
如果`findAny()`返回任何元素,`findFirst()`返回流中的第一个元素。显然,当我们只对流的第一个元素感兴趣时(例如,竞赛的获胜者应该是竞争对手排序列表中的第一个元素),这种方法很有用。 如果`findAny()`返回任何元素,`findFirst()`返回流中的第一个元素。显然,当我们只对流的第一个元素感兴趣时(例如,竞赛的获胜者应该是竞争对手排序列表中的第一个元素),这种方法很有用。
...@@ -2192,7 +2192,7 @@ Java`Stream`API 提供了`filtering()`、`flatMapping()`和`mapping()`,特别 ...@@ -2192,7 +2192,7 @@ Java`Stream`API 提供了`filtering()`、`flatMapping()`和`mapping()`,特别
在概念上,`filtering()`的目标与`filter()`相同,`flatMapping()`的目标与`flatMap()`相同,`mapping()`的目标与`map()`相同。 在概念上,`filtering()`的目标与`filter()`相同,`flatMapping()`的目标与`flatMap()`相同,`mapping()`的目标与`map()`相同。
# 筛选() # `filtering​()`
用户问题:*我想把所有重 2000 克以上的西瓜都按种类分类。对于每种类型,将它们添加到适当的容器中(每种类型都有一个容器—只需检查容器的标签即可)* 用户问题:*我想把所有重 2000 克以上的西瓜都按种类分类。对于每种类型,将它们添加到适当的容器中(每种类型都有一个容器—只需检查容器的标签即可)*
...@@ -2256,7 +2256,7 @@ Map<Boolean, Set<Melon>> melonsFiltering = melons.stream() ...@@ -2256,7 +2256,7 @@ Map<Boolean, Set<Melon>> melonsFiltering = melons.stream()
{false=[Hemi(1600g), Hemi(2000g)], true=[Hemi(2600g)]} {false=[Hemi(1600g), Hemi(2000g)], true=[Hemi(2600g)]}
``` ```
# 映射() # `mapping​()`
用户问题:*对于每种类型的甜瓜,我都需要按升序排列的权重列表* 用户问题:*对于每种类型的甜瓜,我都需要按升序排列的权重列表*
...@@ -2292,7 +2292,7 @@ Map<Boolean, Set<String>> melonsMapping = melons.stream() ...@@ -2292,7 +2292,7 @@ Map<Boolean, Set<String>> melonsMapping = melons.stream()
{false=[Crenshaw, Hemi], true=[Gac, Hemi]} {false=[Crenshaw, Hemi], true=[Gac, Hemi]}
``` ```
# 平面映射() # `flatMapping()`
要快速提醒您如何展开流,建议阅读“映射”部分。 要快速提醒您如何展开流,建议阅读“映射”部分。
...@@ -2373,7 +2373,7 @@ Map<Boolean, Set<String>> pestsFlatMapping = melonsGrown.stream() ...@@ -2373,7 +2373,7 @@ Map<Boolean, Set<String>> pestsFlatMapping = melonsGrown.stream()
} }
``` ```
# 192 发球 # 192 `teeing()`
从 JDK12 开始,我们可以通过`Collectors.teeing()`合并两个收集器的结果: 从 JDK12 开始,我们可以通过`Collectors.teeing()`合并两个收集器的结果:
...@@ -2581,7 +2581,7 @@ public class MelonCollector implements ...@@ -2581,7 +2581,7 @@ public class MelonCollector implements
* `CONCURRENT`:流中的元素可以由多个线程并发地累加(最终收集器可以对流进行并行归约)。流的并行处理产生的容器组合在单个结果容器中。数据源的性质应该是无序的,或者应该有`UNORDERED`标志。 * `CONCURRENT`:流中的元素可以由多个线程并发地累加(最终收集器可以对流进行并行归约)。流的并行处理产生的容器组合在单个结果容器中。数据源的性质应该是无序的,或者应该有`UNORDERED`标志。
* `IDENTITY_FINISH`:表示累加器本身就是最终结果(基本上我们可以将`A`强制转换为`R`),此时不调用`finisher()` * `IDENTITY_FINISH`:表示累加器本身就是最终结果(基本上我们可以将`A`强制转换为`R`),此时不调用`finisher()`
# 供应商——供应商 # 供应者——`Supplier<A> supplier()`
`supplier()`的任务是(在每次调用时)返回一个空的可变结果容器的`Supplier` `supplier()`的任务是(在每次调用时)返回一个空的可变结果容器的`Supplier`
...@@ -2604,7 +2604,7 @@ public Supplier<Map<Boolean, List<Melon>>> supplier() { ...@@ -2604,7 +2604,7 @@ public Supplier<Map<Boolean, List<Melon>>> supplier() {
在并行执行中,可以多次调用此方法。 在并行执行中,可以多次调用此方法。
# 累积元素–双消费者 # 累积元素——`BiConsumer<A, T> accumulator()`
`accumulator()`方法返回执行归约操作的函数。这是`BiConsumer`,这是一个接受两个输入参数但不返回结果的操作。第一个输入参数是当前结果容器(到目前为止是归约的结果),第二个输入参数是流中的当前元素。此函数通过累积遍历的元素或遍历此元素的效果来修改结果容器本身。在我们的例子中,`accumulator()`将当前遍历的元素添加到两个`ArrayList`之一: `accumulator()`方法返回执行归约操作的函数。这是`BiConsumer`,这是一个接受两个输入参数但不返回结果的操作。第一个输入参数是当前结果容器(到目前为止是归约的结果),第二个输入参数是流中的当前元素。此函数通过累积遍历的元素或遍历此元素的效果来修改结果容器本身。在我们的例子中,`accumulator()`将当前遍历的元素添加到两个`ArrayList`之一:
...@@ -2618,7 +2618,7 @@ public BiConsumer<Map<Boolean, List<Melon>>, Melon> accumulator() { ...@@ -2618,7 +2618,7 @@ public BiConsumer<Map<Boolean, List<Melon>>, Melon> accumulator() {
} }
``` ```
# 应用最终转换–函数 # 应用最终转换——`Function<A, R> finisher()`
`finisher()`方法返回在累积过程结束时应用的函数。调用此方法时,没有更多的流元素可遍历。所有元素将从中间累积类型`A`累积到最终结果类型`R`。如果不需要转换,那么我们可以返回中间结果(累加器本身): `finisher()`方法返回在累积过程结束时应用的函数。调用此方法时,没有更多的流元素可遍历。所有元素将从中间累积类型`A`累积到最终结果类型`R`。如果不需要转换,那么我们可以返回中间结果(累加器本身):
...@@ -2631,7 +2631,7 @@ public Function<Map<Boolean, List<Melon>>, ...@@ -2631,7 +2631,7 @@ public Function<Map<Boolean, List<Melon>>,
} }
``` ```
# 并行化收集器–二进制运算符 # 并行化收集器——`BinaryOperator<A> combiner()`
如果流是并行处理的,那么不同的线程(累加器)将生成部分结果容器。最后,这些部分结果必须合并成一个单独的结果。这正是`combiner()`所做的。在这种情况下,`combiner()`方法需要合并两个映射,将第二个`Map`的两个列表中的所有值加到第一个`Map`中相应的列表中: 如果流是并行处理的,那么不同的线程(累加器)将生成部分结果容器。最后,这些部分结果必须合并成一个单独的结果。这正是`combiner()`所做的。在这种情况下,`combiner()`方法需要合并两个映射,将第二个`Map`的两个列表中的所有值加到第一个`Map`中相应的列表中:
...@@ -2648,7 +2648,7 @@ public BinaryOperator<Map<Boolean, List<Melon>>> combiner() { ...@@ -2648,7 +2648,7 @@ public BinaryOperator<Map<Boolean, List<Melon>>> combiner() {
} }
``` ```
# 返回最终结果–函数 # 返回最终结果–`Function<A, R> finisher()`
最终结果用`finisher()`方法计算。在这种情况下,我们只返回`Function.identity()`,因为累加器不需要任何进一步的转换: 最终结果用`finisher()`方法计算。在这种情况下,我们只返回`Function.identity()`,因为累加器不需要任何进一步的转换:
...@@ -2661,7 +2661,7 @@ public Function<Map<Boolean, List<Melon>>, ...@@ -2661,7 +2661,7 @@ public Function<Map<Boolean, List<Melon>>,
} }
``` ```
# 特征–设置<characteristics>特征();</characteristics> # 特征——`Set<Characteristics> characteristics()`
最后,我们指出我们的收集器是`IDENTITY_FINISH``CONCURRENT` 最后,我们指出我们的收集器是`IDENTITY_FINISH``CONCURRENT`
...@@ -2793,7 +2793,7 @@ melons.forEach(m -> Melon.growing100g(m)); ...@@ -2793,7 +2793,7 @@ melons.forEach(m -> Melon.growing100g(m));
melons.forEach(Melon::growing100g); melons.forEach(Melon::growing100g);
``` ```
# 实例方法的方法引用 # 实例方法的方法引用
假设我们为`Melon`定义了以下`Comparator` 假设我们为`Melon`定义了以下`Comparator`
...@@ -2848,7 +2848,7 @@ List<Integer> sorted = melons.stream() ...@@ -2848,7 +2848,7 @@ List<Integer> sorted = melons.stream()
.collect(Collectors.toList()); .collect(Collectors.toList());
``` ```
# 构造器的方法引用 # 构造器的方法引用
可以通过`new`关键字引用构造器,如下所示: 可以通过`new`关键字引用构造器,如下所示:
...@@ -2958,7 +2958,7 @@ double result = customThreadPool.submit( ...@@ -2958,7 +2958,7 @@ double result = customThreadPool.submit(
1000 threads (~ 250 ms) 1000 threads (~ 250 ms)
``` ```
# 分离 # 拆分
Java`Spliterator`接口(也称为*可拆分迭代器*)是用于并行遍历源元素(例如,集合或流)的接口。此接口定义以下方法: Java`Spliterator`接口(也称为*可拆分迭代器*)是用于并行遍历源元素(例如,集合或流)的接口。此接口定义以下方法:
...@@ -3217,7 +3217,7 @@ System.out.println( ...@@ -3217,7 +3217,7 @@ System.out.println(
组合(或链接)函数、谓词和比较器允许我们编写应该统一应用的复合标准。 组合(或链接)函数、谓词和比较器允许我们编写应该统一应用的复合标准。
# 合成谓词 # 组合谓词
假设我们有以下`Melon`类和`Melon``List` 假设我们有以下`Melon`类和`Melon``List`
...@@ -3295,7 +3295,7 @@ List<Melon> result = melons.stream() ...@@ -3295,7 +3295,7 @@ List<Melon> result = melons.stream()
.collect(Collectors.toList()); .collect(Collectors.toList());
``` ```
# 合成比较器 # 组合比较器
让我们考虑上一节中相同的`Melon`类和`Melon``List` 让我们考虑上一节中相同的`Melon`类和`Melon``List`
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册