使用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)
}