Golang代码优化:哪种实现方式更好求建议
Golang代码优化:哪种实现方式更好求建议 我想用Go语言创建链表代码,但不确定应该使用哪种风格。
*** 第一段代码开始 ***
package main
import "fmt"
type List struct {
head *Node
}
type Node struct {
next *Node
element int
}
func (list *List) append(element int) {
if list.head == nil {
list.head = &Node{nil, element}
} else {
node := list.head
for node.next != nil {
node = node.next
}
node.next = &Node{nil, element}
}
}
func (list *List) appendFirst(element int) {
list.head = &Node{list.head, element}
}
func (list *List) Print() {
node := list.head
for node != nil {
fmt.Print(node.element, " ")
node = node.next
}
fmt.Println("")
}
func main() {
ll := List{}
for i := 0; i < 10; i++ {
ll.appendFirst(i)
// ll.append(i)
}
ll.Print()
}
*** 第一段代码结束 ***
*** 第二段代码开始 ***
package main
import "fmt"
type List *Node
type Node struct {
next List
element int
}
func append(list *List, element int) {
if *list == nil {
*list = &Node{nil, element}
} else {
append(&((*list).next), element)
}
}
func appendFirst(list *List, element int) {
*list = &Node{*list, element}
}
func Print(list List) {
node := list
for node != nil {
fmt.Print(node.element, " ")
node = node.next
}
fmt.Println("")
}
func main() {
var list List = nil
for i := 0; i < 10; i++ {
appendFirst(&list, i)
//append(&list, i)
}
Print(list)
}
*** 第二段代码结束 ***
请告诉我应该使用哪种代码风格。
更多关于Golang代码优化:哪种实现方式更好求建议的实战教程也可以访问 https://www.itying.com/category-94-b0.html
如果我是Go开发者,我会使用标准库中的链表。
func main() {
fmt.Println("hello world")
}
更多关于Golang代码优化:哪种实现方式更好求建议的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您的回复。
但这个答案并没有解决我的问题。试想一下,如果您是一名 Go 开发者,您会如何编写一个链表程序。是否有其他更好的程序。
感谢您的回复。
您说得对,我们可以直接使用内置的数据结构来完成。但有些教授正在用 Go 语言教授链表。我给出了基于面向对象编程的答案,但他对我的解决方案并不满意。所以我想,我应该采用基于双指针的过程式解决方案。
你好 @sylvie,
感谢你的回答,我认为你提到了一些关键点。你说的“单一用途包倾向于函数模式”是什么意思?
这个函数模式是什么?确实,strings 包暴露了一些函数(而不是实现接口的对象),但数据结构是一个提供功能的对象。请提供更多细节。
这是一个很棒的想法 @hollowaykeanho。你还应该定义一个包含这些方法的接口,并让你的数据结构实现它。当其他人查看你的代码时,他们会看到这个接口,并对你提供的功能有一个清晰的认识。
方法和函数都是有效的;方法只是一个带有特殊接收者参数的函数。实际上,两者没有太大区别。一些人遵循的经验法则是,在管理状态时使用方法,因为这更自然流畅。请注意,你也可以编写指针接收者来修改接收者所指向的值。
此外,示例2中的这个函数与内置函数
append冲突。你在提交给讲师之前测试过你的程序吗?Go 开发者直接阅读代码就能轻易发现这一点。这会不会让你的讲师感到不快?
func append(list *List, element int) {
感谢您的回复。
您说得对,我们可以直接使用内置的数据结构来完成。但有些教授正在用 Go 语言教授链表。我给出了基于面向对象编程的答案,但他对我的解决方案并不满意。所以我想,我应该采用基于双指针的过程式解决方案。
命名规范我会修正。但问题是,编写代码应该采用过程式还是面向对象的方法。
我的风格
package linkedlist
func NewStack[T any]() *Stack[T] {
return new(Stack[T])
}
type Stack[T any] struct {
length int
head *Iterator[T]
}
func (c *Stack[T]) Next(iter *Iterator[T]) *Iterator[T] {
return iter.next
}
func (c *Stack[T]) Begin() *Iterator[T] {
return c.head
}
func (c *Stack[T]) End(iter *Iterator[T]) bool {
此文件已被截断。显示原文
你好 @hemant_jain,
很高兴看到你在编写这个数据结构,欢迎来到 Go 论坛。
请采用第一种方法,也就是你的第一个实现。我认为第二种实现方式不是 Go 语言中常见的编码风格,我不喜欢 if *list == nil 这种写法,因为它使用了过多的指针,使得代码更难阅读和编写。
现在,在编写任何类型的数据结构时,你都应该编写大量的单元测试。同时,尝试使用泛型(你的链表应该能够存储任何数据类型,而不仅仅是 int)。
另外,请将 append 方法设为公开(Append),并在 append 方法和 Node 数据结构上方添加注释。
hemant_jain:
你说得对,我们可以直接使用内置数据结构来完成。但有些教授正在用 Go 语言教授链表。
你完全确定你的讲师真的期望在 Go 中实现一个链表,还是期望一些跳出常规的解法(这很明显)?
我曾经有一位讲师,在一次结构完整性作业中,当我的同学们都在使用金属丝等其他材料时,他却暗中期望我只用一袋生的意大利面来举起 5+3 公斤的重物。隐藏的期望是在工程技术细节之外,还需要具备成本核算和产品生命周期规划(该建筑只需要维持 10 分钟寿命)等商业敏锐度。
旁注:
如果你的讲师真的期望你用 Go 实现一个链表,我会深深地叹一口气。把它当作人生中的一次模拟吧。有时,生活会给你柠檬。试着把它做成柠檬水。祝你好运。注意:在 Go 中完全可以使用双指针。
hemant_jain:
我之前给出了基于 OOP 的答案
啊…… Go 不是 OOP 语言。它不支持多态或数据类型的继承。方法 1 看起来像 OOP,但其实不是。
这些方法是鸭子类型的接口。你无法更改任何已定义的方法,也无法基于现有定义及其所有方法来定义新的数据类型而不完全重写——只能使用组合。
也许他/她是因为这个原因而感到失望?
哦对了……我差点忘了:在开发一个“不安全”的库时,尽量提供一个完整的创建、读取、更新和删除(CRUD)功能。按理说,链表最困难的部分通常是删除,因为在C语言中,内存是手动管理的。既然你用的是Go,垃圾回收器显然会支持你,所以你需要咨询你的讲师,了解在你的演示中是否需要手动管理内存。
你已经完成了:
Append - 将一个元素添加到列表的末尾
Prepend - 将一个元素添加到列表的开头
String - 将列表以字符串形式打印出来
缺失的API有:
Len / Length - 提供列表的总长度
Index - 按位置读取值
Insert - 按位置添加元素
Pop - 按位置移除元素
Search - 按值扫描索引
Scan - 按值扫描所有索引
Join - 将两个链表合并为一个
Split - 将一个链表拆分为两个不同的链表
Copy - 复制一个链表
Delete / Destroy - 通过释放内存空间来移除所有元素,然后销毁链表
Sanitize - 深度检查每个元素是否可用且安全
Zero - 将所有元素的值设置为零(通常在安全释放时使用,即在释放内存之前将机密数据清零)
Reserve - 在使用前预留并初始化链表中的一组元素(用于音频/视频等对时间敏感的环境)
我相信就这些了。请与你的决策者确认。祝你好运。
从代码可读性和Go语言惯用法的角度来看,第一种实现方式更好。以下是具体分析:
第一种实现的优势:
- 结构清晰:
List作为独立结构体,封装了链表头指针,更符合面向对象的设计思想 - 方法接收器明确:使用指针接收器
(list *List),代码意图清晰 - 内存安全:避免了二级指针操作,减少空指针风险
- Go语言惯用:符合Go标准库中数据结构的实现模式
示例对比:
第一种实现的追加方法更直观:
// 第一种:方法调用简洁
ll := List{}
ll.append(10)
// 第二种:需要传递指针
var list List = nil
append(&list, 10)
性能考虑:
第一种实现的append方法使用循环而非递归,避免了递归调用的栈开销:
// 第一种:迭代方式,效率更高
func (list *List) append(element int) {
if list.head == nil {
list.head = &Node{nil, element}
} else {
node := list.head
for node.next != nil {
node = node.next
}
node.next = &Node{nil, element}
}
}
// 第二种:递归方式,链表长时可能栈溢出
func append(list *List, element int) {
if *list == nil {
*list = &Node{nil, element}
} else {
append(&((*list).next), element)
}
}
扩展性:
第一种实现更容易添加其他链表操作,例如计算长度:
func (list *List) Length() int {
count := 0
node := list.head
for node != nil {
count++
node = node.next
}
return count
}
结论: 选择第一种实现方式,它在可读性、安全性和性能方面都更优,且更符合Go语言的编码规范。



