如何将 Java 流转换为滑动窗口?

2022-09-01 11:36:06

将流转换为滑动窗口的推荐方法是什么?

例如,在 Ruby 中,您可以使用each_cons

irb(main):020:0> [1,2,3,4].each_cons(2) { |x| puts x.inspect }
[1, 2]
[2, 3]
[3, 4]
=> nil
irb(main):021:0> [1,2,3,4].each_cons(3) { |x| puts x.inspect }
[1, 2, 3]
[2, 3, 4]
=> nil

在番石榴中,我只找到了迭代器#partition,它是相关的,但没有滑动窗口:

final Iterator<List<Integer>> partition =
   Iterators.partition(IntStream.range(1, 5).iterator(), 3);
partition.forEachRemaining(System.out::println);
-->
[1, 2, 3]
[4]

答案 1

API中没有这样的函数,因为它支持顺序和并行处理,并且很难为任意流源的滑动窗口函数提供有效的并行处理(即使是有效的对并行处理也非常困难,我实现了它,所以我知道)。

但是,如果您的源是具有快速随机访问的,则可以使用方法获得所需的行为,如下所示:ListsubList()

public static <T> Stream<List<T>> sliding(List<T> list, int size) {
    if(size > list.size()) 
        return Stream.empty();
    return IntStream.range(0, list.size()-size+1)
                    .mapToObj(start -> list.subList(start, start+size));
}

类似的方法实际上在我的StreamEx库中可用:请参阅StreamEx.ofSubLists()

还有一些其他第三方解决方案不关心并行处理,并使用一些内部缓冲区提供滑动功能。例如,protonpack StreamUtils.windowed


答案 2

如果您愿意使用第三方库并且不需要并行性,那么jOOλ提供了SQL样式的窗口函数,如下所示

int n = 2;

System.out.println(
Seq.of(1, 2, 3, 4)
   .window(0, n - 1)
   .filter(w -> w.count() == n)
   .map(w -> w.window().toList())
   .toList()
);

屈服

[[1, 2], [2, 3], [3, 4]]

int n = 3;

System.out.println(
Seq.of(1, 2, 3, 4)
   .window(0, n - 1)
   .filter(w -> w.count() == n)
   .map(w -> w.window().toList())
   .toList()
);

屈服

[[1, 2, 3], [2, 3, 4]]

这是一篇关于它的工作原理的博客文章

免责声明:我为jOOλ背后的公司工作


推荐