【问题标题】:Intrusive list implementation for Java?Java的侵入式列表实现?
【发布时间】:2011-04-13 04:54:41
【问题描述】:

是否有任何(实现良好的)可用于 Java 的侵入式双链表类?还是我应该自己做? Boost 支持 C++:http://beta.boost.org/doc/libs/1_40_0/doc/html/boost/intrusive/list.html

侵入式列表是一个容器,在这种情况下,元素内有(在这种情况下)next 和 prev 指针,因此典型的列表操作(如替换和删除)可以直接针对基本类而不是容器类。在某些特定情况下,侵入式列表是最佳解决方案。

我试着举一个合适的例子。假设我有不同类型的类 X1 和 Y2 拥有的链表 L list1 和 L list2。

类Q(与其他无关或不容易访问x1和Y2的接口)需要做i)替换,ii)删除元素e的操作,它总是存在于list1 xor中的某个地方list2 取决于运行时状态,但该信息不会直接存储在任何地方。

使用侵入式列表 Q 可以只存储对成员元素 e 的元素的引用,并且它总是指向正确的位置。

否则,您必须从几个明显更复杂的解决方法中进行选择。 - 元素 e 的包装类以及完成操作 i 和 ii 的附加方法。没有。

基本上,问题仍然不在于性能,而在于架构复杂性。这也可以理解为一种共享对象情况,解决方案 IL 避免了对每个客户端 Lx 和 Q 的更新需求。

请注意,我不需要与其他标准容器兼容。只是一个通用的侵入式 lsit 实现,具有与未知元素类一起使用的迭代、添加、删除和查找操作。

谢谢。

【问题讨论】:

  • 你能准确解释一下为什么“经典”java.util.LinkedList 对你来说不是一个足够好的 LinkedList 实现吗?还有为什么,哦,为什么,你宁愿不实现 Collection 接口,当它确定实现它会使你的集合可用时,而不是一个更多的 NIY 症状​​。
  • @Riduidel。此外,从 Java 1.6 开始,LinkedList 有了一个很好的替代方案,称为ArrayDeque
  • 侵入式列表的主要好处是,给定列表中的一个元素,它可以在恒定时间内被删除。无需搜索,因为 nextprevious 链接就在元素中。请参阅链接的 Boost 文档中的 iterator_to。还可以节省一些内存,因为列表中的每个条目都不需要额外的包装对象。
  • 谢谢戴夫。我也试着举一个一般的例子。假设我有不同类型的类 X 和 Y 拥有的链表 L1 和 L2。类 Q 需要对元素 e 进行操作,根据运行时状态,它总是存在于其他 L1 xor L2 中,但没有人知道直接哪一个。使用侵入式列表 Q 可以只存储指向成员元素 e 的“指针”,它总是指向正确的位置。
  • ...我的意思是这个操作通常是替换操作。

标签: java list collections linked-list


【解决方案1】:

是否有任何(良好实现的)可用于 Java 的侵入式双链表类?

我不相信你会找到一个。

我对“intrusive”的理解要存储在数据结构中的应用对象需要直接包含(“intrude”)应用程序对象中的 next/prev 指针

这与“指针包装器”方法形成对比,其中数据结构节点包含指向应用程序对象的指针,因此不需要修改应用程序对象。侵入式方法有a number of benefits,包括避免“指针包装器”方法常见的双重解引用(一次用于节点,一次用于节点指向应用程序对象的指针)。

我不相信您会找到用于 Java 的侵入式数据结构库。 Java 缺乏类似 C++ 的模板,也没有多重继承,而且它也不容易支持整个对象的按值复制。因此,我认为没有办法将下一个/上一个实例字段通用地添加到 Java 对象中。

例如,给定应用程序对象:

class Foo {
    int bar;
}

不知何故,您需要能够添加下一个/上一个字段和列表管理方法到它或它的派生:

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 重用来添加列表管理方法。不过,我强烈建议不要使用这种方法。

肥皂盒

您没有说明为什么需要侵入式数据结构。也许您有需要一个的正当理由,但很难想象它们。 Java 严重依赖于使用对象指针,并且像这样试图避免它们是很困难的。

我恭敬地建议您考虑:

  • 您是否尝试过早优化,
  • 您是否添加了不必要的复杂性而没有什么好处
  • 如果速度和内存管理至关重要,那么您使用的语言是否正确?

【讨论】:

    【解决方案2】:

    我不知道任何现有的实现(不,我不认为普通的 Java 集合具有侵入性)。

    这可能是因为这样一个列表在 Java 中的唯一主要优势是快速的 remove() 调用,当您已经拥有要删除的元素时(并且在该位置没有 Iterator) . not-copying-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;
      }
    }
    

    【讨论】:

    • 谢谢。我认为这是正确的答案,尽管我在发布之前就怀疑过。但是当有人有相同的意见时,它总是很舒服。 ;)
    【解决方案3】:

    您希望通过使用侵入性列表获得什么?我很难想象有什么优势。好的,有一个微不足道的效率,即每个节点只有一个对象而不是两个对象。但这样做的代价是灵活性和可靠性方面的重大损失。例如,现在您不能同时在两个列表中拥有相同的对象。如果调用者检索列表成员的实例并尝试更改其中一个指针本身会发生什么?调用者克隆了一个节点并且未能更新指针是什么?等等。

    Dave Ray 指出,使用侵入式列表,您可以在恒定时间内删除一个节点,因为您已经有了指针并且不需要重新查找该项目。是的,但这假设您已经在某处保存了节点的句柄,现在又回到了它。使用 Java LinkedList 类,访问元素的唯一方法几乎是使用迭代器遍历列表,或搜索特定对象。如果您使用迭代器进行遍历,则 Iterator.remove 将在恒定时间内删除。因此,只有当您搜索、找到并决定删除时,您才会支付此罚款。我认为如果他们缓存一个指向最后一个对象的指针来提高性能,这将是对 LinkedList 实现的改进。我的猜测是他们没有这样做,因为这会使删除变得不确定:您可以在列表中多次拥有相同的对象-实际上是相同的实例或比较相等的两个对象-并且当前删除的合同说它总是删除第一次出现。如果有缓存指针,很难说这是第一个、第二个还是第 42 个。

    哦,要实际回答您的问题:不,我不知道那里有开源实现。但是,如果我需要自己的链表风格,我想我会自己动手。据推测,如果标准 Java 不能满足您的需求,那一定意味着您有一些非常专业的需求。在这种情况下,寻找一个恰好满足您的专业需求的现成实现听起来很麻烦,而且您肯定可以在一两天的工作中实现它?您可能会花费更多时间寻找合适的产品,而不是仅仅这样做。您可能已经花更多的时间阅读这篇文章的回复,而不是写这篇文章。

    【讨论】:

    • "如果您使用迭代器遍历,则 Iterator.remove 将在恒定时间内删除。"这是误导。使用迭代器遍历以查找对象是线性的。元素的实际移除仅对于 LinkedLists 是恒定的,但对于 ArrayLists 是线性的(由于必须将所有元素移动到后备数组中已移除元素的右侧)。
    • @DhruvGairola 是的,当我说 iterator.remove 需要恒定时间时,我是在讨论链表而不是数组或任何其他结构的上下文中发言。是的,找到一个对象,而不是删除本身,是线性的。对不起,如果我在这方面误导了任何人。在链表中查找对象是......好吧,假设我想不出任何方法可以让它比线性更快,而不会将它变成一个更复杂的结构,这不是我们通常所说的“链表”。
    【解决方案4】:

    如果您查看 SDK 代码,您会发现 LinkedList 实际上是一个私有类 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;
        }
    }
    

    【讨论】:

    • 这不是侵入性的。侵入式列表在元素本身中具有 next/prev 指针,因此根本不使用 Entry 类。
    【解决方案5】:

    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
    

    【讨论】:

      【解决方案6】:

      你看过Deque class吗?根据 javadoc,“支持两端元素插入和删除的线性集合。名称 deque 是“双端队列”的缩写,通常发音为“deck”。 由ArrayDequeLinkedBlockingDequeLinkedList实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-18
        • 1970-01-01
        • 1970-01-01
        • 2011-04-22
        • 1970-01-01
        • 2010-09-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多