具有通用节点类型的双链表

时间:2019-03-17 11:08:05

标签: java generics

我需要一个可以在不同的节点实现上工作的双向链表。请注意,我不希望包含通用数据的节点,例如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)冲突。我宁愿不使用强制转换,因为我认为这是不好的做法(?),而且因为实现对性能至关重要。当然有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

我发现了另一个不错的解决方案,虽然性能不高,但比上一个解决方案更优雅。

该思想仅使用一种类型的节点,并将各种内容包装在通用容器中。 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;

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