【问题标题】:Doubly Linked List with generic node type具有通用节点类型的双向链表
【发布时间】:2019-03-17 11:08:04
【问题描述】:

我需要一个可以在不同节点实现上工作的双向链表。请注意,我不想要包含通用数据的节点,例如 DoublyLinkedNode<T>,而是像 DoublyLinkedList<N extends DoublyLinkedNode<T>> 这样的节点。

确切地说,我在一般情况下使用带有标准节点的列表,但在我程序的其他部分,我需要带有附加字段的节点。所以我将通用节点实现为

public class DoublyLinkedNode<T> {
    DoublyLinkedNode<T> before, after;
    T value;
}

特殊类型为

public class DoublyLinkedSpecialNode<T, S> extends DoublyLinkedNode<T> {
    S specialValue;
}

现在,在我的 DoublyLinkedList 实现中,我希望能够同时处理这两种情况,因为所有代码基本上都在摆弄两个实现显然共有的指针。

这给出了一些要求:

1) 当我使用特殊节点时,我希望能够将其作为DoublyLinkedSpecialNode 类型返回,以便能够访问其他字段。

2) 列表必须使用 DoublyLinkedNode 类型来访问节点的指针。

3) 列表将指向的节点分配给其他节点,例如head = node.after;,所以特殊节点中的指针类型必须与列表中的类型相同。

扩展列表没有意义,因为那时我无法更改方法的返回类型。因此我尝试了两个想法但没有成功:

已经提到的解决方案:从 DLN 扩展的通用节点类型

列表如下所示:

public class DoublyLinkedList<T, N extends DoublyLinkedNode<T>> {
    N head, tail;
    N tail() {
        return tail; // OK
    }
    void remove(N node) {
        if (head == node) {
            head = node.after; // Type error
        }
...

此解决方案与要求 3 冲突),因为在列表中类型是从 DLN 扩展的 N,但在节点实现 N 中,指针是基类/接口 DLN 的类型(指针类型理论上可以比 N 更一般)。

基础 DLN 而非泛型

在这种情况下,列表在基类节点上工作,并且由于多态性而接受子类:

public class DoublyLinkedList<T> {
    DoublyLinkedNode<T> head, tail;
    DoublyLinkedNode<T> tail() {
        return tail; 
    }
    void remove(DoublyLinkedNode<T> node) {
        if (head == node) {
            head = node.after; // OK
        }
...

但是tail()只能返回一般类型的节点,与1)冲突。我宁愿不使用强制转换,因为我认为这是不好的做法(?),但也因为实现对性能至关重要。肯定有更好的方法吗?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    我找到了另一个解决方案,它还可以,性能不是很好,但比上一个解决方案更优雅。

    这个想法是只使用一种类型的节点,并将不同的内容包装在一个通用容器中。 DLN 代码如下所示:

    public class DoublyLinkedNode<C> {
        DoublyLinkedNode<C> before, after;
        C content;
    
        public static class ValueContent<T> {
            T value;
        }
    
        public static class ValueSpecialContent<T, S> extends ValueContent<T> {
            S specialValue;
        }
    }
    

    列表实现如下所示:

    public class DoublyLinkedList<C> {
        DoublyLinkedNode<C> head, tail;
    
        public DoublyLinkedNode<C> head() {
            return head;
        }
    
        void remove(DoublyLinkedNode<C> node) {
            if (head == node) {
                head = node.after;
    ...
    

    我可以像这样从调用类访问特殊字段:

    DoublyLinkedList<SpecialContent<SpecialType>> list;
    SpecialType s = list.head().content.specialValue;
    

    有一些开销,因为每个节点都必须实例化该容器类,但我认为它仍然比强制转换更好。我必须检查性能影响。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多