動機
(時)系列処理とかでしばしば利用するSliding Window処理をJavaでやりたい。
SQLだと標準で(大半の実装で?)Window関数があるので悩まなくていいのですが、Javaでは無いので本記事を記載します。
StackOverflowにコードはあったのですが、Listでデータを持ってしまっているか、ライブラリを使っているかのどちらかでした。
引用させて頂くと以下のような感じ。
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)); }
このコードの気に入らない点は一つだけです。パラメータがListなところです。これだとメモリの消費が大きくなってしまう場合があります。
コード
特段、工夫せずに素直に書き直しました。
public static <T> void sliding(Stream<T> stream, int size, Consumer<List<T>> consumer) {
Iterator<T> itr = stream.iterator();
List<T> buffer = new LinkedList<T>();
while (itr.hasNext()) {
buffer.add(itr.next());
if (buffer.size() > size)
buffer.remove(0);
consumer.accept(buffer);
}
}
動作確認
呼び出しの例は以下のような感じです。
sliding(Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 3, System.out::println);
実行結果
上記の実行結果は、以下のような感じです。
[0]
[0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
参考資料
StackOverflow - How to transform a Java stream into a sliding window?