dream1849 2019-01-22 04:42
浏览 62
已采纳

在golang中实现通用链表,这不允许同一链表中的不同类型

I want to make a linked list which can hold the values of any type but the linked list must hold values of any one type only.

In general when I am using interfaces for achieving this - any type which implements the interface for the node can be added to the linked list.

I have written an implementation for this where whenever a new key is added to the linked list, the type of the key is checked against the type of the key at the head. I want to understand if this is the correct way of implementing it or is there a better way of doing it.

package main

import (
    "errors"
    "fmt"
    "reflect"
    "strings"
)

type MyNode struct {
    value int
}

func (node *MyNode) PrintValue() {
    fmt.Printf(" %d ", node.value)
}

type llNode struct {
    key        llNodeInterface
    next       *llNode
    llNodeType reflect.Type
}

type llNodeInterface interface {
    PrintValue()
}

type ComplexNode struct {
    realValue  int
    imageValue int
}

func (node *ComplexNode) PrintValue() {
    fmt.Printf(" %d + i%d", node.realValue, node.imageValue)
}

// Student type.
type Student struct {
    name string
    age  int
}

// Student implements the PrintValue function - thus llNodeInterface is implemented.
func (node *Student) PrintValue() {
    fmt.Printf("Name: %s | Age : %d ", node.name, node.age)
}

// Function which will check the of the new node before adding to the linked
// list. It checks the type of the new key against the type of the key in the
// head. If both are equal then it proceed else return error.
func (head *llNode) AddBeforeHeadTypeCheck(passedKey llNodeInterface) error {

    if head.key == nil {
        head.key = passedKey
        head.llNodeType = reflect.TypeOf(head.key)
    } else {
        typeOfPassedKey := reflect.TypeOf(passedKey)

        if typeOfPassedKey != head.llNodeType {
            fmt.Printf("
Unsupported type for the type %T", passedKey)
            return errors.New("Type mistmatch")
        }

        temp := llNode{key: head.key, next: head.next}
        head.key = passedKey
        head.next = &temp
    }
    return nil
}

// Function which will not check the types and will simply add the new node to
// the linked list. Thus linked list will be able to have nodes of multiple
// types.
func (head *llNode) AddBeforeHead(passedKey llNodeInterface) {

    if head.key == nil {
        head.key = passedKey
        head.llNodeType = reflect.TypeOf(head.key)
    } else {
        temp := llNode{key: head.key, next: head.next}
        head.key = passedKey
        head.next = &temp
    }
}

func (head *llNode) Init() {
    head.key = nil
    head.next = nil
    head.llNodeType = nil

}

// Print the linked list.
func (head *llNode) DisplayLL() {

    temp := head
    fmt.Printf("
%s", strings.Repeat("#", 80))
    fmt.Printf("
Printing the linked list
")

    for {
        if temp.key == nil {
            fmt.Println("Linked list is empty")
            break
        } else {
            fmt.Printf("
 %T %v ", temp.key, temp.key)
            key := temp.key
            key.PrintValue()
            if temp.next == nil {
                break
            } else {
                temp = temp.next
            }
        }
    }
    fmt.Printf("
%s", strings.Repeat("#", 80))
    fmt.Printf("

")
}

func testWithMixedType() {
    head := llNode{}
    head.Init()

    for i := 1; i < 10; i++ {
        temp := &ComplexNode{i, i * 10}
        head.AddBeforeHeadTypeCheck(temp)
    }

    temps := &Student{"rishi", 20}
    head.AddBeforeHeadTypeCheck(temps) // Will give error.
    head.DisplayLL()
}

func testWithComplexNumber() {

    head := llNode{}
    head.Init()

    for i := 1; i < 10; i++ {
        temp := &ComplexNode{i, i * 10}
        head.AddBeforeHeadTypeCheck(temp)
    }

}

func main() {
    testWithComplexNumber()
    testWithMixedType()

}

The code is working fine - but I want to understand if there is a better or different way of doing this.

Also - what is the performance impact of the current checking of types using the reflect package. Is there a different way of achieving the same thing.

Unsupported type for the type *main.Student

Unsupported type for the type *main.Student
################################################################################
Printing the linked list

 *main.ComplexNode &{9 90}  9 + i90
 *main.ComplexNode &{8 80}  8 + i80
 *main.ComplexNode &{7 70}  7 + i70
 *main.ComplexNode &{6 60}  6 + i60
 *main.ComplexNode &{5 50}  5 + i50
 *main.ComplexNode &{4 40}  4 + i40
 *main.ComplexNode &{3 30}  3 + i30
 *main.ComplexNode &{2 20}  2 + i20
 *main.ComplexNode &{1 10}  1 + i10
################################################################################
  • 写回答

1条回答 默认 最新

  • duanri1985 2019-01-22 13:52
    关注

    You can do it either by using interfaces and runtime checks (as you've discovered), or by using code generation. These are the current options you have in Go for generic programming. The Go team is working to add generics to the language - it's a work in progress, and everyone is free to participate in the discussion. Once generics exist, they will provide the solution you seek here.

    As for interfaces vs. code generation, there are the performance implications you've mentioned. Code generation will generate tighter code that doesn't need to do runtime checks for most operations; on the other hand, it adds a bit of complexity to the build process of your project. These are the usual tradeoffs of having something resolved at run-time vs. precomputing things at compile-time.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥100 求数学坐标画圆以及直线的算法
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 自己瞎改改,结果现在又运行不了了
  • ¥15 链式存储应该如何解决
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站