我正在使用Java在事件流上实现一个滑动窗口。 所以我想要一个允许我执行以下操作的数据结构:
在发生新事件时添加到数据结构的末尾;
在处理旧事件时从数据结构的开头删除;
获取标准随机访问(size()
,get(i)
)到数据结构的元素;通常,典型的List“阅读”操作;
对所有上述操作都有效;
无界限。
无需其他访问权限。并且不需要线程安全。
我目前正在使用ArrayList执行此操作,以便开始运行。但我想要更高效的东西;使用remove(0)
时,ArrayList
方法(上面的2.)效率很低。
数字1.和2.是标准Queue - 样式的操作。但是,JDK中Queue
的实现(例如ArrayDeque)不允许3中的get(i)
。
所以,我想知道是否有任何库具有这样的实现,并且适合商业用途。
如果没有,我想我会写自己的......
答案 0 :(得分:4)
似乎是Circular Buffer的任务 - 只要队列具有固定容量就可以了。我不知道有任何标准的实现。但这里是a nice recipe to roll your own。
答案 1 :(得分:4)
我遇到了这个问题并试图通过复制ArrayDeque
的源代码并添加类似的内容来解决它:
E get(index){ return elements[(head + index) % size];}
答案 2 :(得分:3)
如果你有一个足够大的数组,你可以实现一个带有基本数组的队列,只需使用一个索引作为列表的头部,并使用mod运算符,这样你就可以在需要时回滚。
所以,你基本上有一个支持插入和删除函数的循环数组。
<强>更新强>
将数组复制到一个更大的数组是一个快速的操作,所以当接近结尾时,可能只是加倍,并且只是复制数组,作为插入操作的一个步骤。总体而言,您仍然可以非常快速地访问,因为常规不应该是增加和复制。
答案 3 :(得分:2)
事件进出这个队列的速度有多快?
一方面,你可以拥有一个“足够大”的循环缓冲区。
虽然它在技术上是“有限的”,但你可以通过在必要时增长来使其“无限制”。
出于同样的原因,当它“安静”时,你可以在整体容量方面“缩小”它。
但是,对于许多应用程序,100,1000甚至10000项容量的循环缓冲区实际上是无限制的。
答案 4 :(得分:1)
只是把它扔到那里作为滚动你自己的替代方案,所以请带着一点点盐:取决于您需要随机访问get(i)
的频率以及您需要的性能(以及它有多大)您的队列大小通常是),当您需要访问元素时,可以始终使用ArrayDeque.toArray()[i]
。 toArray()
使用System.arraycopy()
封面,对于小型队列大小和偶尔使用,应该非常快。有助于理解为什么需要随机访问队列以及需要多长时间 - 可能有一种不同的方法来实现你的算法。
答案 5 :(得分:1)
如果它真的必须是无界的,那么像ConcurrentSkipListMap这样的东西可能很有用,如果你为每个事件分配一个递增序列以用作地图中的键。 它提供了pollFirst / LastEntry等方法。 如果你可以牺牲它的无界性质,那么你可能需要一个环形缓冲区。
答案 6 :(得分:0)
我能想到的唯一可以实现这种接口的库是LinkedList,但坦率地说我不确定性能特征是什么。
答案 7 :(得分:0)
二项式堆可以有O(1)摊销的插入和O(log n)摊销删除min;我相信它也可以有O(log ** 2 n)摊销的随机访问。 Queue-push会将元素插入堆中,并将连续的整数作为键。
使用rbtree,您可以对O(log n)进行队列推送,对所有插入,删除min和随机访问都是悲观的。这是因为树将有一个连续的整数集作为键,而队列的第k个元素将是树中带有第k个键的元素。