是否有适用于Java的(良好实现的)侵入式双链表类?或者我应该自己做? Boost为C ++提供了它:http://beta.boost.org/doc/libs/1_40_0/doc/html/boost/intrusive/list.html。
Intrusive list是一个容器,在这个容器中有(在这种情况下)next和prev指针,因此像replace和remove这样的典型列表操作可以直接定位到基本类而不是容器类。在某些情况下,侵入式列表是最佳解决方案。
我试着给出一个恰当的例子。假设我有不同类型的X1和Y2所拥有的链表L list1和L list2。
类Q(没有任何关系或者不容易访问x1和Y2的接口)否则需要做i)替换,ii)删除元素e的操作,它总是存在于某个地方,在list1 xor中list2取决于运行时状态,但该信息不会直接存储在任何地方。
使用intrussive列表Q可以将元素的引用存储到成员元素e,它总是指向正确的位置。
否则,您必须从几个明显更复杂的解决方法中选择一个或另一个。 - 元素e的包装类和完成操作i和ii的其他方法。否。
基本上,问题仍然不是性能而是架构复杂性。这也可以理解为一种共享对象情况,其中解决方案IL避免了对每个客户端Lx和Q的更新需求。
请注意我不需要与其他标准容器兼容。只是一个通用的侵入式lsit实现,具有与未知元素类一起使用的迭代,添加,删除和查找操作。
感谢。
答案 0 :(得分:6)
我不知道任何现有的实现(不,我不认为普通的Java集合是侵入性的。)
这可能是因为当您已经将元素移除(并且没有remove()
时,这样的列表在Java中唯一的主要优势就是快速Iterator
调用在那个位置)。 not-copy-the-element不是Java中的有效参数,因为Java List
实现只处理引用(并且永远不会复制整个对象)。
但您可以通过创建必要的界面轻松编写一个通用的List
实现:
public interface IntrusiveListElement<E extends<IntrusiveListElement<E>> {
public void setNext(E next);
public E getNext();
public void setPrev(E prev);
public E getPrev();
}
public class IntrusiveList<E extends IntrusiveListElement<E>> implements List<E> {
// implement your run-of-the-mill double-linked list here
}
您的元素类可能如下所示:
public class MyBusinessElement implements IntrusiveListElement<MyBusinessElement> {
private MyBusinessElement prev;
private MyBusinessElement next;
public void setNext(MyBusinessElement next) {
this.next = next;
}
public MyBusinessElement getNext() {
return next;
}
public void setPrev(MyBusinessElement prev) {
this.prev = prev;
}
public MyBusinessElement getPrev() {
return prev;
}
}
答案 1 :(得分:5)
是否有适用于Java的(实现良好的)侵入式双链表类?
我不相信你会找到一个。
我对 “侵入性” 的理解是 要存储在数据结构中的应用程序对象需要直接包含(“intrude”)应用程序对象中的下一个/上一个指针 。
这与“指针包装器”方法形成对比,其中数据结构节点包含指向应用程序对象的指针,因此不需要修改应用程序对象。侵入式方法具有a number of benefits,包括避免对“指针包装器”方法通用的双重解除引用(一次用于节点,一次用于节点指向应用程序对象的指针)。
我不相信你会找到一个针对Java的侵入式数据结构库。 Java缺乏类似C ++的模板,也没有多重继承,并且它不容易支持整个对象的按值复制。因此,我认为没有办法将下一个/ prev实例字段一般性地添加到Java对象中。
例如,给定应用程序对象:
class Foo {
int bar;
}
不知何故,您需要能够向其添加next / prev字段和列表管理方法或其衍生物:
class Foo2 {
int bar;
Foo2 prev;
Foo2 next;
}
通过为这些应用程序类提供扩展的基类来添加这些字段的一种方法 - 这是Boost的方法。但是,这种方法在Java等单继承语言中非常有限。
Java接口通常是Java对多重继承的回答,例如:一个需要应用程序类的getNext()和getPrev()方法的接口。但是,如果出于性能原因需要侵入式数据结构,通过方法访问next / prev字段可能会对这些目标产生负面影响。
Java泛型也不会以所需的方式扩展类。
或者我应该自己动手?
如果有一次针对特定情况需要仔细评估,请确保自己动手。如果你试图将通用的一个用于通用目的,我不确定它是否值得。
一个非常重要的方法是自定义扩展应用程序类以添加所需的字段:
class Foo3 extends Foo {
Foo3 prev;
Foo3 next;
}
并使用cut-n-paste重用来添加列表管理方法。但是,我强烈建议使用此方法 not 。
<强> 肥皂盒 强>
您没有说明为什么需要侵入式数据结构。也许你有正当理由需要一个,但很难想象它们。 Java非常依赖于使用对象指针并试图避免它们,因为这很难。
我恭敬地建议你考虑一下:
答案 2 :(得分:1)
如果您查看SDK代码,您会看到LinkedList实际上是包含next和previous元素的私有类Entry的列表。 因此,如果在此列表中包含MyClass,则列表中的元素将是包含您的对象的条目以及列表的下一个和上一个元素的链接。
所以,我认为它是侵入性的......
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
答案 3 :(得分:0)
您希望通过使用侵入式列表获得什么?我很难想到它的优点是什么。好的,有一个微不足道的效率,你只有一个对象而不是每个节点两个。但这样做的代价是灵活性和可靠性的巨大损失。例如,现在您不能同时在两个列表中拥有相同的对象。如果调用者检索列表成员的实例并尝试更改其中一个指针本身会发生什么?调用者克隆节点并且无法更新指针的内容是什么?等
Dave Ray指出,使用侵入式列表,您可以在固定时间内删除节点,因为您已经拥有指针并且不需要重新找到该项目。没错,但是假设你已经在某个地方保存了一个句柄,现在又回到了它。使用Java LinkedList类,访问元素的唯一方法是使用Iterator遍历列表,或搜索特定对象。如果使用迭代器遍历,Iterator.remove将在恒定时间内移除。因此,只有当您搜索,查找,然后决定删除您支付此罚款时。我认为如果它们缓存了指向最后一个对象的指针,那将是对LinkedList实现的一种改进,这个对象在这一点上提高了性能。我的猜测是他们没有,因为它会使删除不确定:你可以多次在列表中有相同的对象 - 字面上相同的实例或两个比较相等的对象 - 和目前删除的合同说它总是删除第一次出现。如果存在缓存指针,则很难说这是第一个,第二个还是第42个。
哦,实际上回答你的问题:不,我不知道那里有一个开源实现。但是如果我需要自己的链接列表的味道,我想我只是自己动手。据推测,如果标准Java不符合您的需求,那必定意味着您有一些非常专业的需求。在这种情况下,搜索恰好满足您的专业需求的现成实现听起来很麻烦,您肯定可以在一天或两天的工作中实现它?您可能花费更多时间寻找合适的产品,而不是仅仅这样做。你可能已经花了更多的时间阅读这篇文章的回复,而不是写你的回复。
答案 4 :(得分:-1)
你看过Deque class了吗?根据javadoc,“支持两端元素插入和删除的线性集合。名称deque是”双端队列“的缩写,通常发音为”deck“。” 它由ArrayDeque,LinkedBlockingDeque和LinkedList实施。
答案 5 :(得分:-1)
Java的语义是设计侵入性的,它们使用引用而不复制。你基本上只想找到java所拥有的最好的链表实现,LinkedList或其他什么,它会是侵入性的。
import java.util.LinkedList;
public class Foo {
public static void main(String[] args) {
String[] i = new String[1];
i[0] = "FOO";
String[] j = new String[1];
j[0] = "BAZ";
LinkedList<String[]> list = new LinkedList<String[]>();
list.add(i);
list.add(j);
for (String[] x: list) {
System.out.println(x[0]);
}
i[0] = "ZILCH";
for (String[] x: list) {
System.out.println(x[0]);
}
}
}
这是输出,请注意我如何更改列表中的值。
FOO
BAZ
ZILCH
BAZ