Java 一种高效的数组插入/删除算法
我订阅了一个数据提要,并从中使用INSERT/DELETE消息上的索引值创建和维护一个结构。我想问一下组装的认知专家,他们是否知道有任何算法能够以有效的方式处理零碎的更新——通常批量更新包含两到六条这样的消息 阵列的估计大小约为1000个元素 批量更新以按索引排序的消息列表的形式到达,该列表规定在给定索引处插入或删除项。我预计阵列中的大多数搅动将更接近开始而不是结束 我突然想到,通过一些基本处理,我可以确定受批次和总体大小增量影响的范围,因此只需移动一次未受影响的阵列尾部 类似地,我可以在第一个元素之前和最后一个元素之后保持一定的可用空间,以尽可能少地进行复制 其他优化包括识别更新,例如:Java 一种高效的数组插入/删除算法,java,arrays,algorithm,performance,random-access,Java,Arrays,Algorithm,Performance,Random Access,我订阅了一个数据提要,并从中使用INSERT/DELETE消息上的索引值创建和维护一个结构。我想问一下组装的认知专家,他们是否知道有任何算法能够以有效的方式处理零碎的更新——通常批量更新包含两到六条这样的消息 阵列的估计大小约为1000个元素 批量更新以按索引排序的消息列表的形式到达,该列表规定在给定索引处插入或删除项。我预计阵列中的大多数搅动将更接近开始而不是结束 我突然想到,通过一些基本处理,我可以确定受批次和总体大小增量影响的范围,因此只需移动一次未受影响的阵列尾部 类似地,我可以在第一个
DELETE 10, INSERT 10 - effectively a replace which requires no copying
INSERT 10, DELETE 11 - as above
DELETE 10, DELETE 10, DELETE 10 - bulk deletion can be optimised into one copy operation
INSERT 11, INSERT 12, INSERT 13 - bulk insertion can be optimised into one copy operation
等等
然而,我对执行识别步骤的开销很谨慎——这有点像是看头和跟踪,这可能比简单地执行复制要花更多的时间
考虑到阵列的预期大小,树结构似乎很重要:一些基本的性能测试表明,二进制或自平衡树(在本例中为红黑树列表实现)只有在15K-20K个元素之后才开始显示性能优势:在较小的大小下,阵列拷贝的速度明显更快。我可能应该补充一点,我正在使用Java进行这个实现
欢迎任何提示、提示或建议
干杯
Mike最简单的方法是在应用更新时运行更新并将阵列复制到新阵列中 1000不是那么大,可能不值得进一步优化
为了让您的生活更轻松,除了对单个更新进行排序(正如您已经提到的)之外,还可以更好地使用
阵列列表来尝试和整合内容,我不知道我会有多麻烦。坦率地说,在大范围内,1000个元素算不了什么。我有一个包含2500万个元素的系统,使用简单的批量拷贝,而且(出于我们的目的)远远不够快
因此,我不会戴上“预成熟优化”的帽子,但我可能会先在书架上浏览一下。使用链接列表(java.util.LinkedList
)可能是值得研究的。在一个特定的索引中获取一个元素当然是昂贵的,但它可能比执行数组拷贝要好。始终权衡代码的清晰度和优化。如果现在没有性能问题,只需确保代码清晰即可。如果将来出现性能问题,您将知道它的确切性质。现在做准备是一种猜测
如果您需要进行相当多的操作,那么一个链表可能是值得的
但是,对于简单清晰的代码,我将对原始数组或arraylist使用apache commons collection UTIL,否则:
myArray = ArrayUtils.add(myArray, insertionIndex, newItem);
或
ArrayList mylist=newarraylist(Arrays.asList(myArray));
添加(insertionIndex,newItem);
有一种非常简单的数据结构,名为“笛卡尔树”或“Treaps”,它允许在数组上进行O(logn)拆分、连接、插入和删除(以及许多其他操作)
2-3个树的实现也非常简单(我对一个稍微复杂的工具的实现在第一次编译后只有一个bug),并且适合您的目的。如果空间不是一个约束,并且您不会有重复的树,那么就使用Set datastructure,特别是Java的HashSet
。这种数据结构的强大之处在于插入和删除都是在O(1)时间内完成的,如果性能是“标准”,这将最适合您
此外,除了快速检索之外,每当谈到阵列时,都会遇到大量阵列副本的严重限制,这不仅会占用空间(用于阵列增长),而且效率也会很低,因为每次插入/删除都可能需要O(n)个时间。通常,如果您有按索引顺序列出的更改,则可以构建一个只复制一次的简单循环。下面是一些伪代码:
array items;
array changes; // contains a structure with index, type, an optional data members
array out; // empty, possibly with ensureCapacity(items.length)
int c = 0, delta = 0;
// c is the current change
//delta tracks how indexing has changed by previous operations
for (i = 0; i < items.length; i++) {
if c < changes.length {
curchange = changes[c]
if (i + delta) == curchange.index {
c++;
if (curchange.type == INSERT) {
out.add(curchange.data)
delta--;
} else {
delta++;
continue; // skip copying i
}
}
}
out.add(items[i])
}
for (; c < changes.length; c++) { // handle trailing inserts
assert(c.index == out.length && c.type == INSERT)
out.add(c.data);
}
数组项;
数组更改;//包含具有索引、类型和可选数据成员的结构
数组输出;//空,可能带有ensureCapacity(items.length)
int c=0,delta=0;
//c是当前的变化
//增量跟踪以前的操作如何更改索引
对于(i=0;i
它在输入数组中运行一次,并使用所做的所有更改构建输出数组
请注意,这不会在同一位置处理多个插入。这样做会使代码更复杂一些,但并不难
但是,它将始终在整个阵列中运行,每批运行一次。一个稍微艰难的改变是保持一个临时的状态,并用两个指数变量进行适当的改变;然后,如果你命中了更改列表的末尾,你可以提前退出循环,而不接触列表的其余部分。 < P>如果这确实是你的数据集的样子,你可以考虑用一个集合(如HasMMAP)进行重复跟踪。数组将是带有
array items;
array changes; // contains a structure with index, type, an optional data members
array out; // empty, possibly with ensureCapacity(items.length)
int c = 0, delta = 0;
// c is the current change
//delta tracks how indexing has changed by previous operations
for (i = 0; i < items.length; i++) {
if c < changes.length {
curchange = changes[c]
if (i + delta) == curchange.index {
c++;
if (curchange.type == INSERT) {
out.add(curchange.data)
delta--;
} else {
delta++;
continue; // skip copying i
}
}
}
out.add(items[i])
}
for (; c < changes.length; c++) { // handle trailing inserts
assert(c.index == out.length && c.type == INSERT)
out.add(c.data);
}
class EventQueue
{
Vector eventQueue;
HashMap eventMap;
public synchronized Event getNextEvent()
{
Event event = eventQueue.remove(0);
eventMap.remove(event.getId()); // this would be 10 from 'INSERT 10'
// in the sample from the OP
}
public synchronized addEvent(Event e)
{
if( eventMap.containsKey(e.getId())
{
// replace events that already exist
int idx = eventMap.get(e.getId());
eventQueue.removeElementAt(idx);
eventQueue.add(idx, e);
} else {
// add new events
eventQueue.add(e);
eventMap.add(e.getId(), eventQueue.size()); // may be off by one...
}
}
public boolean isReady()
{
return eventQueue.size() > 0;
}
}
class FeedListener extends Thread {
EventQueue queue;
EventFeed feed;
...
public void run()
{
while(running) {
sleep(sleepTime);
if( feed.isEventReady() ) {
queue.addEvent(feed.getEvent());
}
}
}
}
abstract class EventHandler extends Thread {
EventQueue queue;
...
public void run()
{
while(running)
{
sleep(sleepTime);
if( queue.isReady() )
{
Event event = queue.getNextEvent();
handleEvent(event);
}
}
}
public abstract void handleEvent(Event event);
}