简体   繁体   English

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

[英]Doubly Linked List with generic node type

I need a doubly linked list that can work on different node implementations. 我需要一个可以在不同节点实现上使用的双向链表。 Note that I do not want nodes that contain generic data, like DoublyLinkedNode<T> , but something like DoublyLinkedList<N extends DoublyLinkedNode<T>> . 请注意,我不希望包含通用数据的节点,如DoublyLinkedNode<T> ,但像DoublyLinkedList<N extends DoublyLinkedNode<T>>

To be precise, I use the list in the general case with standard nodes, but in other parts of my program I need nodes with additional fields. 确切地说,在通常情况下,我将列表与标准节点一起使用,但在程序的其他部分中,我需要具有附加字段的节点。 So I implemented the general node as 所以我将通用节点实现为

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

and the special type as 和特殊类型为

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

Now in my DoublyLinkedList implementation I want to be able to handle both cases at one, because all the code basically fiddles around with the pointers that are obviously common to both implementations. 现在在我的DoublyLinkedList实现中,我希望能够在一个实例中处理这两种情况,因为所有代码基本上都会使用两个实现明显相同的指针。

This gives a few requirements: 这给出了一些要求:

1) When I use the special node, I want to be able to return it as the DoublyLinkedSpecialNode type to be able to access the additional fields. 1)当我使用特殊节点时,我希望能够将其作为DoublyLinkedSpecialNode类型返回,以便能够访问其他字段。

2) The list must use a DoublyLinkedNode type to access the pointers of the nodes. 2)该列表必须使用DoublyLinkedNode类型来访问节点的指针。

3) The list is assigning the pointed-to nodes to other nodes, eg head = node.after; 3)列表将指向的节点分配给其他节点,例如head = node.after; , so the type of the pointers in the special node must be the same as the type in the list. ,因此特殊节点中指针的类型必须与列表中的类型相同。

Extending the list makes no sense because then I could not change the return type of the methods. 扩展列表毫无意义,因为我无法更改方法的返回类型。 Therefore I tried two ideas without success: 因此,我尝试了两个想法,但没有成功:

The already mentioned solution: Generic node type that extends from DLN 已经提到的解决方案:从DLN扩展的通用节点类型

The list would look like this: 列表看起来像这样:

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
        }
...

This solution conflicts with requirement 3), because in the list the type is an N that extends from DLN, but in the node implementation N the pointer is of the type of the base class/interface DLN (the pointer type could theoretically be more general than N). 此解决方案与要求3)冲突,因为在列表中类型是从DLN扩展的N,但是在节点实现N中,指针是基类/接口DLN的类型(理论上,指针类型可以更通用)比N)。

Base DLN instead of generics 基本DLN而非通用

In this case the list works on the base class node and accepts the subclasses because of polymorphism: 在这种情况下,列表在基类节点上工作,并且由于多态性而接受子类:

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
        }
...

But tail() can only return nodes as the general type, conflicting with 1). 但是tail()只能返回一般类型的节点,与1)冲突。 I would prefer not to use casting, because I assume it's bad practice (?) but also because the implementation is performance critical. 我宁愿不使用强制转换,因为我认为这是不好的做法(?),但也因为实现对性能至关重要。 There surely is a better way? 当然有更好的方法吗?

I found another solution that is okay-ish, not very performant but more elegant than the last solution. 我找到了另一种解决方案,它是好的,不是很高效,但比上一个解决方案更优雅。

The idea ist to use only one type of nodes, and wrap the varying content in a generic container. 这个想法只是使用一种类型的节点,并将不同的内容包装在一个通用的容器中。 The DLN code looks like this: 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;
    }
}

The list implementation then looks something like this: 然后列表实现看起来像这样:

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;
...

And I can access the special field from the calling class like this: 我可以从调用类访问特殊字段,如下所示:

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

There is some overhead because each node has to instantiate that container class, but I think it's still better than casting. 有一些开销,因为每个节点都必须实例化该容器类,但是我认为它仍然比强制转换更好。 I have to check the performance impact. 我必须检查性能影响。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM