diff --git a/src/main/java/com/thealgorithms/datastructures/buffers/SlidingWindowAggregator.java b/src/main/java/com/thealgorithms/datastructures/buffers/SlidingWindowAggregator.java new file mode 100644 index 000000000000..ae20c88cd334 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/buffers/SlidingWindowAggregator.java @@ -0,0 +1,447 @@ +package com.thealgorithms.datastructures.buffers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.BinaryOperator; +import java.util.function.Function; + +/** + * A fixed-capacity ring (circular) buffer that additionally keeps an aggregate of every element it + * currently holds, i.e. a sliding window with aggregation. + * + *
The window slides automatically: once the buffer is full, adding a new element evicts the + * oldest one, and the aggregate is kept in sync without rescanning the window. + * + *
The naive way to keep a windowed sum is "add the new value, subtract the evicted one". That + * trick only works for invertible operators: there is no way to subtract a value from a + * minimum. This class instead uses the classic two-stack sliding window aggregation + * algorithm, which needs nothing but associativity: + * + *
A pleasant side effect is numerical stability: because aggregates are recomputed from scratch + * on every flip, floating-point error cannot accumulate indefinitely the way it does with the + * add-then-subtract approach. + * + *
| Operation | Complexity |
|---|---|
| {@link #add(Object)} | O(1) amortised, O(n) worst case |
| {@link #removeOldest()} | O(1) amortised, O(n) worst case |
| {@link #aggregate()} | O(1) |
| {@link #get(int)}, {@link #peekOldest()}, {@link #peekNewest()} | O(1) |
| memory | O(capacity), no allocation after construction |
{@code
+ * // Maximum of the last three readings.
+ * SlidingWindowAggregator window = SlidingWindowAggregator.maximum(3, Comparator.naturalOrder());
+ * for (int value : new int[] {1, 3, 2, 5, 0}) {
+ * window.add(value);
+ * System.out.println(window.aggregate());
+ * }
+ * // prints 1, 3, 3, 5, 5
+ *
+ * // Moving average of the last 100 samples: the window knows both the sum and its own size.
+ * SlidingWindowAggregator sum = SlidingWindowAggregator.sumOfDoubles(100);
+ * sum.add(sample);
+ * double movingAverage = sum.aggregate() / sum.size();
+ * }
+ *
+ *