Java提供了List接口的LinkedList实现,它实际上是一个双向链表。如果我们执行以下操作:
linkedlist.remove(obj);
然后:
linkedlist.add(obj);
我们实际上从链表中删除对象obj并从右端(尾部)重新插入。
我们还可以手动实现带有节点,头部和尾部的链表。在诸如C ++之类的具有一些低级特征的语言中,可以使用指针来指示obj对象的下一个和前一个对象。因此,我们实际上没有删除项目,只是更新上一个和下一个指针。
Java中是否有任何数据结构可以产生相同的效果(因此只删除对象本身的“指针”的性能提升)?
请注意,我想使用一个随时可用的数据结构,而不是手动编写我的一个链表实现(并且可能重新发明轮子)。此外,请注意,它不一定是一个链表 - 例如,它可能是某种队列,如ArrayDeque。
编辑:换句话说,如果在Java内部List接口的LinkedList实现内部使用prev和next指针,那么为什么l.remove(obj)是O(n)而不是O(1) ?因此在实践中,当你有一个包含数百万个对象的LinkedList时(如我的情况),这需要很长时间才能进行删除和重新插入? (与ArrayList相同,与ArrayDeque相同 - 很长时间)。
答案 0 :(得分:4)
Java与C ++完全相同。对对象的所有引用都是指针。所以,在像
这样的节点中public class Node [
private Object value;
private Node next;
private Node previous;
}
value
,next
和previous
分别是指针(下一个节点和前一个节点)的指针(在Java中称为引用)。
与C ++的不同之处在于你没有指针 arithmetics :value++
,例如,不编译并且不会使指针引用位于下一个内存地址。
编辑:
LinkedList类不会将其节点暴露给外部。它们对LinkedList完全是私有的。因此,删除对象包括迭代所有节点以找到具有与给定对象相等(对于Object.equals()
)的值的节点,并从列表中删除找到的节点。删除节点包括将前一个点指向下一个节点,反之亦然。这就是删除对象是O(n)的原因。当然,如果您有权访问节点并且能够将其删除,则操作将为O(1)。如果需要,您必须实现自己的LinkedList,就像在C ++中一样。参考文献是指针。
答案 1 :(得分:1)
回答有关为什么remove(Object obj)
为O(n)的问题:
首先,为了使其为O(1),您需要提供remove
实际的Node
引用,而不是Object
。 Object
不会指向Node
。为了找到要返回的正确Node
,代码必须搜索列表以查找包含该对象的Node
,或者保留一个哈希映射,以便找到Node
。实际的LinkedList
实现执行简单的线性搜索。但是,即使是哈希映射在技术上也是O(n),因为列表中的所有对象都有可能具有相同的哈希码,尽管它仍然比线性搜索快得多。
其次,remove(Object obj)
是根据equals
定义的。如果列表中添加了不同的对象obj2
,并且obj2.equals(obj)
为true
,那么remove(obj)
如果obj2
将删除obj
引用本身从未添加到列表中。
要真正做到这一点,您需要一个返回节点引用的add
方法,以便您的程序可以跟踪节点引用并将其用作remove
参数;或者您可以要求列表中的对象实现某种NodePointer
接口:
interface NodePointer {
void setNodePointer(Object node);
Object getNodePointer();
}
然后该列表将用于将节点指针填充到对象中。 (但这可能意味着一个对象一次只能存在于一个链表上,这是LinkedList
不强加的限制。)在任何一种情况下,我都认为这不是Java库所支持的。