我想实现一个非常简单的滑动窗口。换句话说,我会有一些列表,其中的对象从该列表的右端插入并从左端删除。在每次插入中,先前的对象被左移一个索引。当列表被对象填充时,在每次从右端插入时,一个对象将从左端删除(并且前一个对象当然会像往常一样左移一个索引。)
我想到了LinkedList或者ArrayDeque - 可能后者是一个更好的选择,因为据我所知,插入和从任一端移除是对ArrayDeque的持续努力O(1),这是不是LinkedList的情况。是吗?
此外,我想问一下:当我插入一个新对象时左移所有存储在滑动窗口中的对象是一个处理密集型的大型滑动窗口,在我的情况下有100,000甚至1,000,000个对象。是否有其他数据结构可能在我的应用程序中表现更好?
注意:我使用术语"滑动窗口"对于我想要实现的内容,也许还有一些其他术语可以更好地描述它,但我认为从上面的描述中我想要做的是明确的。
答案 0 :(得分:5)
ArrayDeque做你想要的。它不会移动元素。它移动开始和结束的索引。添加元素时,结束计数器会移动,当您删除元素时,启动计数器会移动。
ArrayDeque的一个优点是它可以使用更少的内存并创建垃圾。在不利方面,它具有固定的最大尺寸。 LinkedList增长和缩小。
BTW如果你想要一个轻量级滑动窗口或一些值的平均值,指数加权移动平均值要便宜得多,因为你只需要记录上一次和上一次的两个值。
e.g
double last = 0;
long lastTime = 0;
double halfLife = 60 * 1000; // 60 seconds for example.
public static double ewma(double sample, long time) {
double alpha = Math.exp((lastTime - time) / halfLife);
lastTime = time;
return last = sample * alpha + last * (1 - alpha);
}
或者您可以对此进行近似以避免使用
调用Math.exppublic static double ewma(double sample, long time) {
long delay = time - lastTime
double alpha = delay >= halfLife ? 1.0 : delta / halfLife;
lastTime = time;
return last = sample * alpha + last * (1 - alpha);
}
速度提高了很多倍,间隔时间短得多,效果也相同。
答案 1 :(得分:0)
你在谈论Queue吗?看一下java.util.LinkedList
implementation,因为它实现了Queue
接口。同样LinkedList
的推送和弹出复杂度都是O(1),但得到的是O(N)。
编辑:这是LinkedList的添加方法的核心:
Link<ET> next = link.next;
Link<ET> newLink = new Link<ET>(object, link, next);
link.next = newLink;
next.previous = newLink;
link = newLink;
lastLink = null;
pos++;
expectedModCount++;
list.size++;
list.modCount++;