go结构体和接口的小项目

使用map和结构体。做一个记录学生成绩的小项目:

因为map是无序的。我们在输出记录的学生时候,可能第一遍,张三在前边,第二次 张三就在后边了。为了有固定的输出,我们在输出时候,把学生重新存入切片,并对切片按成绩进行排序。然后在输出切片内容。

从Go 1.8开始,sort包引入了Slice函数,它允许你直接对切片进行排序,而无需实现任何接口。项目中我们使用这个函数对 数据进行排序 后输出。

项目代码:

go 复制代码
package main

import (
	"fmt"
	"sort"
)

type stu struct {
	score float64
	name  string
}

type studentMgr struct {
	allStudent map[string]*stu
}

func (s *studentMgr) showStudent() {
	fmt.Println("按照成绩从高到底的排序:")
	stus := make([]*stu, len(s.allStudent))
	var index int32 = 0
	for _, v := range s.allStudent {
		stus[index] = v
		index++
	}
	sort.Slice(stus, func(i, j int) bool { return stus[i].score > stus[j].score })

	for i, v := range stus {
		fmt.Printf("序号:%-4d学生姓名:%-12s分数:%.2f\n", i+1, v.name, v.score)
	}
}

func (s *studentMgr) addStudent() {
	var (
		stu stu
	)
	fmt.Print("请输入姓名:")
	fmt.Scanln(&stu.name)
	fmt.Print("请输入分数:")
	fmt.Scanln(&stu.score)
	s.allStudent[stu.name] = &stu
}

func (s *studentMgr) searchStudent(name string) bool {
	_, ok := s.allStudent[name]
	return ok
}

func (s *studentMgr) modifyStudent() {
	var (
		stu stu
	)
	fmt.Print("请输入姓名:")
	fmt.Scanln(&stu.name)
	if condition := s.searchStudent(stu.name); !condition {
		fmt.Println("没有找到该学生")
		return
	}
	fmt.Print("请输入分数:")
	fmt.Scanln(&stu.score)
	s.allStudent[stu.name] = &stu
}

func (s *studentMgr) deleteStudent() {
	var (
		name string
	)
	fmt.Print("请输入要删除的学生姓名:")
	fmt.Scanln(&name)
	if condition := s.searchStudent(name); !condition {
		fmt.Println("没有找到该学生")
		return
	}
	delete(s.allStudent, name)
}

func newStudentMgr() *studentMgr {
	return &studentMgr{
		allStudent: make(map[string]*stu, 100),
	}
}

func showMenu() {
	fmt.Println("欢迎光临学生管理系统")
	fmt.Println("1.查看所有学生")
	fmt.Println("2.添加学生")
	fmt.Println("3.修改学生")
	fmt.Println("4.删除学生")
	fmt.Println("5.显示菜单")
	fmt.Println("6.退出")
}

func main() {
	var (
		s     *studentMgr
		input string
	)
	s = newStudentMgr()
	showMenu()
	for {
		//如果输入错误的值,input在Scanln中得不到赋值,
		// 使用的是上一次的值。所以这里需要重置input的值
		input = "0"
		fmt.Print("请输入你要干啥:")
		fmt.Scanln(&input)
		switch input {
		case "1":
			s.showStudent()
		case "2":
			s.addStudent()
		case "3":
			s.modifyStudent()
		case "4":
			s.deleteStudent()
		case "5":
			showMenu()
		case "6":
			return
		default:
            showMenu()
			fmt.Println("sorry,你的输入有误,请重新输入")
			
		}
	}

}

​​

Go 语言实现的链表,通过接口 Value 支持多种数据类型,并提供添加、删除和遍历功能:

go 复制代码
package main

import "fmt"

// 定义数据接口
type Value interface {
	Equal(other Value) bool // 用于比较是否相等
	String() string         // 用于打印输出
}

// 实现链表节点
type Node struct {
	Data Value
	Next *Node
}

// 实现链表结构
type LinkedList struct {
	Head *Node
}

// 添加元素到链表尾部
func (ll *LinkedList) Add(data Value) {
	newNode := &Node{Data: data}
	if ll.Head == nil {
		ll.Head = newNode
		return
	}

	current := ll.Head
	for current.Next != nil {
		current = current.Next
	}
	current.Next = newNode
}

// 删除第一个匹配的元素
func (ll *LinkedList) Remove(data Value) bool {
	if ll.Head == nil {
		return false
	}

	if ll.Head.Data.Equal(data) {
		ll.Head = ll.Head.Next
		return true
	}

	prev := ll.Head
	current := ll.Head.Next
	for current != nil {
		if current.Data.Equal(data) {
			prev.Next = current.Next
			return true
		}
		prev = current
		current = current.Next
	}
	return false
}

// 遍历打印链表
func (ll *LinkedList) Display() {
	current := ll.Head
	for current != nil {
		fmt.Print(current.Data.String(), " -> ")
		current = current.Next
	}
	fmt.Println("nil")
}

// 查找元素是否存在
func (ll *LinkedList) Contains(data Value) bool {
	current := ll.Head
	for current != nil {
		if current.Data.Equal(data) {
			return true
		}
		current = current.Next
	}
	return false
}

// 获取链表长度
func (ll *LinkedList) Length() int {
	count := 0
	current := ll.Head
	for current != nil {
		count++
		current = current.Next
	}
	return count
}

func main() {
	// 初始化链表
	list := &LinkedList{}

	// 添加不同类型的数据
	list.Add(IntValue(42))
	list.Add(StringValue("Hello"))
	list.Add(IntValue(100))

	// 打印链表
	fmt.Print("初始链表: ")
	list.Display() // 42 -> Hello -> 100 -> nil

	fmt.Println("数组中是是否包含100", list.Contains(IntValue(100)))
	fmt.Println("数组中是是否包含200", list.Contains(IntValue(200)))
	fmt.Println("数组长度", list.Length())
	// 删除操作
	list.Remove(IntValue(100))
	fmt.Print("删除100后: ")
	list.Display() // 42 -> Hello -> nil

	// 添加新数据
	stu := Student{
		"张三",
		80.5,
	}
	list.Add(stu)
	fmt.Print("添加stu后: ")
	list.Display() // 42 -> Hello -> 张三考了80.50分  -> nil
}

type Student struct {
	name  string
	score float64
}

func (s Student) Equal(other Value) bool {
	if v, ok := other.(Student); ok {
		return s.name == v.name
	}
	return false
}

func (s Student) String() string {
	return fmt.Sprintf("%s考了%.2f分", s.name, s.score)
}

type IntValue int

func (i IntValue) Equal(other Value) bool {
	if v, ok := other.(IntValue); ok {
		return i == v
	}
	return false
}

func (i IntValue) String() string {
	return fmt.Sprintf("%d", i)
}

// 2. 字符串类型
type StringValue string

func (s StringValue) Equal(other Value) bool {
	if v, ok := other.(StringValue); ok {
		return s == v
	}
	return false
}

func (s StringValue) String() string {
	return string(s)
}

关键设计说明:

接口定义

type Value interface {

Equal(other Value) bool

String() string

}

Equal:用于比较数据是否相等(替代泛型的类型约束)

String:用于格式化输出

数据类型实现

每个需要存入链表的数据类型(如 IntValue、StringValue)都需实现 Value 接口:

type IntValue int

type StringValue string

链表操作

添加元素:直接操作指针维护链表结构

删除元素:通过遍历找到目标节点并修改指针

类型安全:通过接口方法 Equal 确保比较操作类型安全

添加更多数据类型

只需让新类型实现 Value 接口:

go 复制代码
type FloatValue float64



func (f FloatValue) Equal(other Value) bool {

if v, ok := other.(FloatValue); ok {

return f == v

}

return false

}



func (f FloatValue) String() string {

return fmt.Sprintf("%.2f", f)

}

相关推荐
亚洲第一中锋_哈达迪5 小时前
深入理解 roaring bitmap
后端·go
懒得更新6 小时前
TORM-查询构建器
go
一个热爱生活的普通人6 小时前
Go 微服务基石:context.Context 设计模式与最佳实践
后端·go
程序员爱钓鱼7 小时前
Go语言实战案例:使用Gin框架构建Hello API
后端·google·go
岁忧1 天前
(LeetCode 面试经典 150 题) 104. 二叉树的最大深度 (深度优先搜索dfs)
java·c++·leetcode·面试·go·深度优先
懒得更新1 天前
TORM-数据库支持
go
Code季风1 天前
GORM 一对多关联(Has Many)详解:从基础概念到实战应用
数据库·go·orm
DemonAvenger1 天前
边缘计算场景下Go网络编程:优势、实践与踩坑经验
网络协议·架构·go