我正在构建一个允许偶尔作弊的事件队列(在现有事件之前或之后插入)。
如果作弊很少见,请选择您自己的基于数组的结构。 java.util.ArrayDeque 在 O(1) 中处理两端,但不能在中间进行插入。它们可以很容易地添加,但复杂度是 O(n)。
请注意,LinkedList 对于大多数操作(由于其糟糕的数据局部性)比ArrayList 和ArrayDeque 慢得多。
假设,您非常需要在中间插入恒定时间。类似的东西
insertAfter(existingElement, newElement);
java.util.LinkedList的问题是它没有这个操作。它只有ListIterator,它允许您在中间插入,但这只有在您首先定位迭代器时才有帮助(O(n))。
所以你需要你自己的链表。基本的实现相当简单,你还需要直接访问它的节点(包含next 和prev 链接的东西,而不是你add 的东西)。然后维护一个Map<MyThing, MyListNode>,它可以让您找到existingElement 的节点。现在您只需为 newElement 创建一个新节点,正确链接它(这基本上是微不足道的,但有一些极端情况)并更新您的地图。
要消除边界情况(第一个/最后一个/唯一节点),请将两个虚拟节点(prefirst 和 postlast)添加到您的链接列表中。显然,这些是唯一不会存储在您的地图中的节点。它们使实现变得非常简单,例如,
void insertAfter(MyThing existingElement, MyThing newElement) {
Node found = map.get(existingElement);
if (found == null) throw ...
Node newNode = new Node(newElement);
map.put(newElement, newNode);
// Link newNode where it belongs to.
newNode.next = found.next;
newNode.prev = found;
// Fix links.
newNode.prev.next = newNode;
newNode.next.prev = newNode;
}
void insertFirst(MyThing newElement) {
Node found = prefirst;
// All the remaining code is simply copied from above.
// Factor it out.
Node newNode = new Node(newElement);
map.put(newElement, newNode);
// Link newNode where it belongs to.
newNode.next = found.next;
newNode.prev = found;
// Fix links.
newNode.prev.next = newNode;
newNode.next.prev = newNode;
}