指针作为映射值
本课属于 Coddy GO 学习旅程的“逻辑与流程”单元。第 27 课,共 68 课。
虽然在映射中直接存储结构体很有用,但存储指向结构体的指针还能带来一个强大的功能:通过映射直接修改原始结构体数据。当你需要在存储数据后更新数据时,这种方法非常重要。
当你将结构体指针存储为映射的值时,你存储的是结构体的内存地址,而不是它的副本。下面介绍如何声明和使用这样的映射:
type Product struct {
Price float64
Quantity int
}
products := map[string]*Product{
"laptop": &Product{Price: 999.99, Quantity: 5},
"mouse": &Product{Price: 25.50, Quantity: 20},
}当你需要修改数据时,这个主要优势就很明显了。由于你使用的是指针,通过映射所做的更改会影响原始结构体:
// 直接通过映射更新笔记本电脑的价格
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // 输出:899.99这种模式在需要频繁更新存储数据的应用中尤其有用,例如库存系统、用户档案,或地图作为主要数据存储且修改需要持久保存的任何场景。
挑战
简单构建一个简单的库存管理系统,展示将结构体指针用作映射值的优势。这个挑战会说明,基于指针的映射如何支持直接修改存储的数据,让库存系统能够高效、及时地响应更新。
你会收到 3 个输入:
- 一个表示产品数量的字符串(例如,
"4") - 一个包含产品数据的字符串,格式为:
"name1:price1:quantity1,name2:price2:quantity2,name3:price3:quantity3"(例如,"laptop:999.99:5,mouse:25.50:20,keyboard:75.00:15,monitor:299.99:8") - 一个包含更新操作的字符串,格式为:
"operation1:product1:value1,operation2:product2:value2",其中操作可以是"price"或"quantity"(例如,"price:laptop:899.99,quantity:mouse:25,price:keyboard:65.00")
你的任务是:
- 定义一个
Product结构体,包含以下字段:Price(float64),表示产品价格Quantity(int),表示产品库存数量
- 创建一个映射,键为产品名称(字符串),值为指向
Product结构体的指针 - 解析第二个输入中的产品数据:
- 按逗号拆分输入,获取各个产品条目
- 对每个条目按冒号拆分,分离名称、价格和数量
- 将价格字符串转换为 float64,将数量字符串转换为整数
- 创建一个
Product结构体,并将指向它的指针存入映射
- 解析时,将产品名称按它们在输入中出现的顺序存入单独的切片。在 Go 中遍历映射时,键的访问顺序是随机的,因此需要用切片来保持所有产品的输入顺序。
- 显示初始库存,并使用标题
"Initial Inventory:" - 按以下格式显示每个产品:
"[name]: $[price] (Stock: [quantity])" - 解析并应用第三个输入中的更新操作:
- 按逗号拆分输入,获取各个操作
- 对每个操作按冒号拆分,获取操作类型、产品名称和新值
- 通过映射中的指针直接更新相应字段
- 按以下格式打印每次更新:
"Updated [product]: [field] changed to [new_value]"
- 显示更新后的库存,并使用标题
"Updated Inventory:" - 按与初始显示相同的格式再次显示每个产品
- 计算并显示库存总价值:
"Total Inventory Value: $[total_value]"
使用 strconv 包将价格和数量字符串转换为相应的数值类型。显示价格时,将所有价格格式化为 2 位小数。这个挑战展示了将指针存入映射后,如何直接修改原始结构体数据,从而立即持久地更新库存,而不需要将值重新赋给映射。
亲手试试
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
// 读取输入
var numProductsStr string
var productDataStr string
var operationsStr string
fmt.Scanln(&numProductsStr)
fmt.Scanln(&productDataStr)
fmt.Scanln(&operationsStr)
// TODO:在这里定义 Product 结构体
// TODO:创建 map 来存储产品指针
// TODO:创建切片来维护产品顺序(确保输出顺序一致)
// TODO:解析产品数据并填充 map
// 注意:
// 用逗号拆分 productDataStr,得到各个条目
// 对每个条目,用冒号拆分出名称、价格和数量
// 将价格和数量的字符串转换为合适的数据类型
// 在 map 中存储指向 Product 结构体的指针
// 将产品名称添加到 order 切片
// TODO:显示初始库存
// 使用顺序切片按固定顺序遍历商品
// 格式:"[name]: $[price] (Stock: [quantity])"
// TODO:解析并执行更新操作
// 注意:
// 用逗号拆分 operationsStr,得到每个操作
// 对每个操作,用冒号拆分,得到类型、名称和值
// 通过 map 中的指针直接更新对应字段
// 为每个操作打印更新消息
// TODO:显示更新后的库存
// 同样使用顺序切片,确保输出顺序一致
// 显示时计算总价值
// TODO:计算并显示库存总价值
// 格式:"Total Inventory Value: $[total_value]"
}这节课包含一个小测验。开始学习即可作答,并记录你的进度。
逻辑与流程中的所有课
自己练习: Go 在线编译器