我有一个问题,我认为它对于流和/或 lambda 来说是完美的。另一方面,我不想让这个变得过于复杂,但由于将在许多变体中使用这种特定的技术(在子列表上运行函数),我想知道一些关于如何从一开始就正确使用它的想法。
我有一个
List<Product> productList
。
我希望能够迭代
productList
中的所有子列表。例如,所有大小=30 的子列表。然后应该将该子列表用作函数的参数。
这是我当前的、天真的解决方案:
List<Product> products=...
// This example uses sublists of size 30
for (int i = 0; i < products.size() - 29; i++) {
// sublist start index is inclusive, but end index is exclusive
List<Product> sublist = products.subList(i, i + 30);
Double res = calc(sublist);
}
// an example of a function would be moving average
如何使用 lambda 来实现这一点?
编辑 我试图想出一个最简单的例子来说明这个问题。经过一些评论后,我意识到一个完美的例子是计算移动平均线。第一个 MAVG 在子列表 [0..29] 上计算,第二个在 [1..30] 上计算,第三个在 [2..31] 上计算,依此类推。
除非我遗漏了一些明显的东西......
IntStream.range(0, products.size() - 29)
.mapToObj(i -> products.subList(i, i + 30))
.map(list -> calc(list))
.forEach... // or any other terminal op
如果您想对这些子列表运行多个函数,例如:
double result = calc(sublist)
log(sublist) // void
double res = diffCalc(sublist)
您可能不再使用通常的 for 循环。
a
map
操作对子列表执行单个操作,可以使其在流中执行更多操作,但这在我看来真的很难看。
如果您不介意使用第三方库,
StreamEx中有一个方法
subLists
可以完全满足您的需求:
List<Product> products = ...
Stream<List<Product>> lists = StreamEx.ofSubLists(products, 30, 1);
我认为使用 lambda 代码可能不太清晰。也许普通的旧if比现代的lambda更好?
int sizeSubList = 30;
for (int i = 0; i < products.size(); i++)
calc(products.subList(i, Math.min(i + sizeSubList, products.size())));
JEP 461:Stream Gatherers Java 22 预览语言功能可用于实现所需的结果:
List<Double> results = productList.stream()
.gather(Gatherers.windowSliding(30))
.map(sublist -> calc(sublist))
.toList();
Stream.gather
方法和新的内置 Gatherers.windowSliding
收集器将地图的条目集从 Stream<Entry>
转换为成对 Stream<List<Entry>>
。然后使用现有的 String[]
方法将每个二元素列表转换为二元素
Stream.map
。
使用子列表大小 3 和对子列表中的值进行平均的函数进行流演练:
[
Product(value = 0),
Product(value = 1),
Product(value = 2),
Product(value = 3),
Product(value = 4)
]
->
[
[Product(value = 0), Product(value = 1), Product(value = 2)],
[Product(value = 1), Product(value = 2), Product(value = 3)],
[Product(value = 2), Product(value = 3), Product(value = 4)]
]
->
[1, 2, 3]
Gatherer
:
将输入元素流转换为输出元素流的中间操作,可以选择在到达上游末尾时应用最终操作。 […]
[…]
聚集操作的例子有很多,包括但不限于:将元素分组(窗口函数);对连续相似的元素进行去重;增量累加功能(前缀扫描);增量重新排序功能等。类
提供了常见收集操作的实现。Gatherers
Stream.gather
:
返回一个流,其中包含将给定收集器应用于该流的元素的结果。
Gatherers.windowSliding
返回一个 Gatherer,它将元素收集到给定大小的窗口(遇到有序的元素组)中,其中每个后续窗口都包含前一个窗口的所有元素(除了最近的元素之外),并在流中添加下一个元素。 […]
示例:
// will contain: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8]] List<List<Integer>> windows2 = Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(2)).toList(); // will contain: [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]] List<List<Integer>> windows6 = Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(6)).toList();