如何使用 Lambda 表达式 .reduce() 方法减少给定列表
How to reduce given list by using Lambda expression .reduce() method
List<Integer> integers = Arrays.asList(1, 2, 3, 5, 6, 8, 9, 10);
integers.stream().filter((integer) -> integer % 2 == 0).collect(Collectors.toList());
如上图integers
是一个List,我们只需要从中过滤出偶数即可。我可以通过使用 .filter()
方法来实现。但是,有没有可能用 .reduce()
方法达到同样的效果。希望,.reduce()
方法通过执行给定的 BynaryOperation 和 return 简化列表来过滤掉所有其他元素。
如果我对.reduce()
方法的理解不正确,请告诉我这个方法到底做了什么。
您可以使用Stream.reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
方法,该方法接受三个参数:
- identity:标识元素既是归约的初始值,也是流中没有元素时的默认结果。在您的情况下,它将是 一个空列表。
- accumulator:累加器函数有两个参数:归约的部分结果和流的下一个元素(在本例中为整数)。它应用对模 2 的检查,然后 returns 一个新的部分结果。
- combiner:它的目的是合并正在并行处理的流批处理的内部临时收集器-累加器。
例如:
BinaryOperator<ArrayList<Integer>> combiner = (x, y) -> { x.addAll(y); return x; };
BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>> accumulator = (x, y) -> {
if (y % 2 == 0) {
x.add(y);
}
return x;
};
List<Integer> list = Stream.of(1, 2, 3, 5, 6, 8, 9, 10).reduce(new ArrayList<Integer>(),
accumulator,
combiner);
System.out.println(list);
请注意,此解决方案可能不适用于并行流。此外,坚持 .filter()
方法太容易了,所以我强烈建议您这样做。
你对还原的理解是错误的。 reduce
将对所有元素重复应用一个函数以获得 一个结果。
你好像很想reduce
1, 2, 3, 5, 6, 8, 9, 10
│ │ │ │ │ │ │ │
└op┘ └op┘ └op┘ └op┘
│ │ │ │
result list
而事实上,确实如此
1, 2, 3, 5, 6, 8, 9, 10
│ │ │ │ │ │ │ │
└op┘ └op┘ └op┘ └op┘
│ │ │ │
└─op─┘ └─op─┘
│ │
└────op────┘
│
final result value
不过,这只是一个概念视图,未指定具体的操作顺序。顺序执行将类似于 (((1 op 2) op 3) op 4)…
,而并行执行将是像上面的树一样的执行和部分顺序执行的混合。
如果您首先将每个元素转换为 List
,然后使用连接每个列表的列表操作,则可以滥用 reduce
来创建结果列表,但是,这样做有两个问题:
- 它没有提供所需的“跳过每个第二个元素(原始列表的)”逻辑;如果你看上面的树,你就会清楚,不可能制定一个正确的
op
函数来在每个可能的执行场景中做到这一点
- 创建临时列表并将它们串联起来效率非常低
后一点可以通过使用 collect
来解决,这是一个 mutable 减少,因此,允许您使用可以添加项目的可变列表,但是,它没有解决第一点,包括所需的过滤器将违反合同并且只能在顺序执行中工作。
所以解决方案是为源列表范围内的所有元素定义一个 filter
,然后使用 collect
进行可变缩减以创建结果列表,而且,大惊喜,这正是您的原始代码所做的:
… .filter(integer -> integer % 2 == 0).collect(Collectors.toList());
List<Integer> integers = Arrays.asList(1, 2, 3, 5, 6, 8, 9, 10);
integers.stream().filter((integer) -> integer % 2 == 0).collect(Collectors.toList());
如上图integers
是一个List,我们只需要从中过滤出偶数即可。我可以通过使用 .filter()
方法来实现。但是,有没有可能用 .reduce()
方法达到同样的效果。希望,.reduce()
方法通过执行给定的 BynaryOperation 和 return 简化列表来过滤掉所有其他元素。
如果我对.reduce()
方法的理解不正确,请告诉我这个方法到底做了什么。
您可以使用Stream.reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
方法,该方法接受三个参数:
- identity:标识元素既是归约的初始值,也是流中没有元素时的默认结果。在您的情况下,它将是 一个空列表。
- accumulator:累加器函数有两个参数:归约的部分结果和流的下一个元素(在本例中为整数)。它应用对模 2 的检查,然后 returns 一个新的部分结果。
- combiner:它的目的是合并正在并行处理的流批处理的内部临时收集器-累加器。
例如:
BinaryOperator<ArrayList<Integer>> combiner = (x, y) -> { x.addAll(y); return x; };
BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>> accumulator = (x, y) -> {
if (y % 2 == 0) {
x.add(y);
}
return x;
};
List<Integer> list = Stream.of(1, 2, 3, 5, 6, 8, 9, 10).reduce(new ArrayList<Integer>(),
accumulator,
combiner);
System.out.println(list);
请注意,此解决方案可能不适用于并行流。此外,坚持 .filter()
方法太容易了,所以我强烈建议您这样做。
你对还原的理解是错误的。 reduce
将对所有元素重复应用一个函数以获得 一个结果。
你好像很想reduce
1, 2, 3, 5, 6, 8, 9, 10
│ │ │ │ │ │ │ │
└op┘ └op┘ └op┘ └op┘
│ │ │ │
result list
而事实上,确实如此
1, 2, 3, 5, 6, 8, 9, 10
│ │ │ │ │ │ │ │
└op┘ └op┘ └op┘ └op┘
│ │ │ │
└─op─┘ └─op─┘
│ │
└────op────┘
│
final result value
不过,这只是一个概念视图,未指定具体的操作顺序。顺序执行将类似于 (((1 op 2) op 3) op 4)…
,而并行执行将是像上面的树一样的执行和部分顺序执行的混合。
如果您首先将每个元素转换为 List
,然后使用连接每个列表的列表操作,则可以滥用 reduce
来创建结果列表,但是,这样做有两个问题:
- 它没有提供所需的“跳过每个第二个元素(原始列表的)”逻辑;如果你看上面的树,你就会清楚,不可能制定一个正确的
op
函数来在每个可能的执行场景中做到这一点 - 创建临时列表并将它们串联起来效率非常低
后一点可以通过使用 collect
来解决,这是一个 mutable 减少,因此,允许您使用可以添加项目的可变列表,但是,它没有解决第一点,包括所需的过滤器将违反合同并且只能在顺序执行中工作。
所以解决方案是为源列表范围内的所有元素定义一个 filter
,然后使用 collect
进行可变缩减以创建结果列表,而且,大惊喜,这正是您的原始代码所做的:
… .filter(integer -> integer % 2 == 0).collect(Collectors.toList());