Go复合数据类型学习总结
文章目录
- 数组
- 切片
- append 函数
- Map
- 结构体
- JSON
数组
数组长度是固定的
Go函数在接收参数时,接收的是参数的副本,所以不会改变原有参数值,在Python中把列表作为参数传递会改变原有列表的值,在Go中不会。
如果想要改变原始的参数值,可以使用指针作为参数传递:
func zero(ptr *[32]byte) {*ptr = [32]byte{} }
遍历数组
for i, v := range a {fmt.Printf("%d %d\n", i, v)
}// Print the elements only.
for _, v := range a {fmt.Printf("%d\n", v)
}
初始化
数组的每个元素都被初始化为元素类型对应的零值,也可以使用数组字面值语法用一组值来初始化数组:
var q [3]int = [3]int{1, 2, 3} var r [3]int = [3]int{1, 2} fmt.Println(r[2]) // "0"
如果在数组的长度位置出现的是**“…”省略号**,则表示数组的长度是根据初始化值的个数来计算:
q := [...]int{1, 2, 3} fmt.Printf("%T\n", q) // "[3]int"
数组的长度必须在定义的时候声明,因为数组的长度需要在编译阶段确定:
q := [3]int{1, 2, 3} q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int
使用
key value
形式初始化:type Currency intconst (USD Currency = iota // 美元EUR // 欧元GBP // 英镑RMB // 人民币 )symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}fmt.Println(RMB, symbol[RMB]) // "3 ¥"
数组比较
如果数组类型是一样(包括元素类型和数组长度),那么这两个数组是可以比较的:
a := [2]int{1, 2} b := [...]int{1, 2} c := [2]int{1, 3} fmt.Println(a == b, a == c, b == c) // "true false false" d := [3]int{1, 2} fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int
切片
Go中的切片操作和Python中的差不多,但是Go中的切片有个
cap
容量的概念和len
长度的概念:如定义一个12个元素的数组:
months := [...]string{1: "January", /* ... */, 12: "December"}
使用该数组的切片的最大容量就是13,该切片的最大长度就是13,如下定义了一个容量为7的切片(6到12的长度),长度为3:
summer := months[6:9]
当切片操作超出容量时就会报异常,但是超出长度,没超出容量就会扩展切片:
// 下边切片长度为20,超出了summer切片底层数组的容量 fmt.Println(summer[:20]) // panic: out of range// 下边切片长度为5,超出了summer切片的长度,但是没有超出summer底层数组的容量,会扩展切片 endlessSummer := summer[:5] // extend a slice (within capacity) fmt.Println(endlessSummer) // "[June July August September October]"
切片只是对底层数组部分元素的引用,切片操作赋值给变量会生成新的数组,直接把切片作为参数传递给函数,可以在函数内部直接对底层数组进行操作:
// reverse reverses a slice of ints in place. func reverse(s []int) {for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {s[i], s[j] = s[j], s[i]} }
a := [...]int{0, 1, 2, 3, 4, 5} reverse(a[:]) fmt.Println(a) // "[5 4 3 2 1 0]"
切片不能通过==直接判断是否相等
长度或者容量为0的切片一定为nil,但是不为nil的切片长度也可能为0,所以不能通过是否等于nil来判断切片长度是否为空:
var s []int // len(s) == 0, s == nil s = nil // len(s) == 0, s == nil s = []int(nil) // len(s) == 0, s == nil s = []int{} // len(s) == 0, s != nil
可以使用
make
创建匿名切片:// 如果不指定容量,默认容量和长度一样 make([]T, len)make([]T, len, cap) // same as make([]T, cap)[:len]// 如果切片的容量是已知的创建时最好指定容量 make([]string, 0, 5)
append 函数
可以通过
append
函数向切片中增加元素:var runes []rune for _, r := range "Hello, 世界" {runes = append(runes, r) } fmt.Printf("%q\n", runes) // "['H' 'e' 'l' 'l' 'o' ',' ' ' '世' '界']"
但是一定要将append结果赋值给原切片:
package mainimport "fmt"func main() {var runes []runevar new_runes []runefor _, r := range "HELLO" {runes = append(runes, r)}fmt.Printf("%q\n", runes) // 输出 ['H' 'E' 'L' 'L' 'O']fmt.Printf("%q\n", new_runes) // 输出 [] }
如果不赋值:
package mainimport "fmt"func main() {var runes []runevar new_runes []runefor _, r := range "HELLO" {new_runes = append(runes, r)}fmt.Printf("%q\n", runes) // 输出 []fmt.Printf("%q\n", new_runes) // 输出 ['O'] }
append 也可以同时追加多个元素:
var x []int x = append(x, 1) x = append(x, 2, 3) x = append(x, 4, 5, 6) x = append(x, x...) // append the slice x fmt.Println(x) // "[1 2 3 4 5 6 1 2 3 4 5 6]"
模拟stack:
空栈
stack := []int
压栈
stack = append(stack, v) // push v
栈顶元素
top := stack[len(stack)-1] // top of stack
弹栈
stack = stack[:len(stack)-1] // pop
Map
Go
中的Map
和Python
中的字典是一样的,Python
字典中的key
要求为不可变类型,但是Go
中的key
只要可以使用== 比较就行。- 不能对
Map
元素取址操作,因为元素并不是一个变量,而且如果Map扩容会导致元素地址重新分配。 - map 和切片一样不能直接通过== 比较是否相等
- map是无序的
创建
// 使用make创建
ages := make(map[string]int)
// 使用var 创建
var new_ages map[string]int
在创建时也可以指定初始值:
ages := map[string]int{"alice": 31,"charlie": 34, }
在创建空map时如果没有初始化,创建完的map为
nil
类型, 长度为0ages := make(map[string]int) var new_ages map[string]int
需要注意的是nil 类型的map 只能进行查询和删除,遍历操作,不能进行增改操作
var nil_map map[string]int// 往使用var创建的nilmap中插入元素报错 nil_map["zzy"] = 2 // panic: assignment to entry in nil map
新增
ages["alice"] = 31
ages["charlie"] = 34
删除
delete(ages, "alice")
查改
// 即使没有bob key 也不会报异常,而是返回对应value类型的0值
ages["bob"] = ages["bob"] + 1
ages["bob"] += 1
ages["bob"]++// 判断是不存在key 返回0还是本来就是0值:
ages := make(map[string]int)ages["zzy"] = 23_, ok_zzy := ages["zzy"]
_, ok_bob := ages["bob"]if !ok_zzy {fmt.Printf("%v:map不存在key为zzy", ok_zzy) // false:map不存在key为zzy
}
if !ok_bob {fmt.Printf("%v:map不存在key为bob", ok_bob)
}// if 可以简写:
if age, ok := ages["zzy"]; !ok {/*...*/}
遍历map
ages := make(map[string]int)ages["alics"] = 31
ages["Alics"] = 32var names []string
var age []int
for key, value := range ages {names = append(names, key)age = append(age, value)
}
自定义key
map 中的可以必须是可以比较类型,如果想使用切片或者其他不可比较类型作为key, 可以自定义函数,将该类型转换为同样含义的不可变类型,在作为key就可以了:
var m = make(map[string]int)// 定义函数k 将切片类型转换为字符串类型作为key func k(list []string) string { return fmt.Sprintf("%q", list) }// 自定义一些需要根据key操作的函数 func Add(list []string) { m[k(list)]++ } func Count(list []string) int { return m[k(list)] }
结构体
结构体定义
- 使用关键字
type
和struct
type Employee struct {ID intName stringAddress stringDoB time.TimePosition stringSalary intManagerID int
}var dilbert Employee
- 一行定义一个类型,相同类型可以定义在同一行,但是通常只将相关的成员写到一起:
type Employee struct {ID intName, Address stringDoB time.TimePosition stringSalary intManagerID int
}
成员顺序不同,将是两个不同的结构体。
成员名字大写表示公开的,小写表示私有的。
结构体成员不能包含自身类型的成员,但可以包含自身指针类型的成员(用于递归)
一个零值结构体的成员都是零值
一个结构体相当于Python中的一个对象,使用结构体定义变量相当于实例化对象,可以通过实例化对象访问结构体中的属性,并且赋值。
dilbert.Salary -= 5000
通过指针访问:
position := &dilbert.Position *position = "Senior " + *position
结构体字面值初始化
根据成员位置初始化:
package p type T struct{ A, B int }package q import "p" var _ = p.T{1, 2} // 一般像定义坐标点,像素这种成员位置不变的结构体使用这种方式初始化
一般使用键值对方式初始化:
package p type T struct{ A, b int }package q import "p" var _ = p.T{A: 1} // 需要注意的是b 为私有属性外部包无法访问,也不能进行初始化
定义函数返回结构体指针
可以定义函数返回指向结构体的指针:
func EmployeeByID(id int) *Employee { /* ... */ }fmt.Println(EmployeeByID(dilbert.ManagerID).Position) // "Pointy-haired boss"id := dilbert.ID EmployeeByID(id).Salary = 0 // fired for... no real reason
如果将函数的从指针类型改为Employee值类型,将编译错误:EmployeeByID(id).Salary = 0(EmployeeByID(id).Salary不是一个变量,不能赋值)
结构体作为函数参数和返回值
func Scale(p Point, factor int) Point {return Point{p.X * factor, p.Y * factor}
}fmt.Println(Scale(Point{1, 2}, 5)) // "{5 10}"
如果结构体较大,可以使用结构体指针作为参数:
func Bonus(e *Employee, percent int) int {return e.Salary * percent / 100 }
如果要修改结构体成员的话,用指针传入是必须的(所有函数参数都是值拷贝传入,不是原始变量):
func AwardAnnualRaise(e *Employee) {e.Salary = e.Salary * 105 / 100 }
结构体比较
和切片、map不同,如果结构体中的所有成员都是可以比较的,那么该结构体可以使用==比较:
type Point struct{ X, Y int }p := Point{1, 2} q := Point{2, 1} fmt.Println(p.X == q.X && p.Y == q.Y) // "false" fmt.Println(p == q) // "false"
可比较的结构体类型可以作为map的key:
type address struct {hostname stringport int }hits := make(map[address]int) hits[address{"golang.org", 443}]++
结构体嵌入和匿名成员
Go 虽然不是一个面向对象的语言,但是却保存了面向对象的优点,去除了面向对象继承的后遗症,因此引入了结构体嵌套的语法,来建立各个对象之间的联系。
一个结构体可以引入另一个结构体:
type Point struct {X, Y int }type Circle struct {Center PointRadius int }type Wheel struct {Circle CircleSpokes int }
访问wheel 成员和嵌套结构体的成员:
var w Wheel w.Circle.Center.X = 8 w.Circle.Center.Y = 8 w.Circle.Radius = 5 w.Spokes = 20
上边的嵌套方式在访问嵌套结构体成员的时候非常复杂,因此引入了匿名成员(只声明一个成员对应的数据类型而不指名成员的名字):
type Circle struct {PointRadius int }type Wheel struct {CircleSpokes int }
访问嵌套成员(可以直接访问嵌套成员,之前的访问方法还是成立的):
var w Wheel w.X = 8 // equivalent to w.Circle.Point.X = 8 w.Y = 8 // equivalent to w.Circle.Point.Y = 8 w.Radius = 5 // equivalent to w.Circle.Radius = 5 w.Spokes = 20
结构体字面值不能使用简短方式初始化匿名成员:
w = Wheel{8, 8, 5, 20} // compile error: unknown fields w = Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields
可以通过以下方式初始化匿名成员:
w = Wheel{Circle{Point{8, 8}, 5}, 20}w = Wheel{Circle: Circle{Point: Point{X: 8, Y: 8},Radius: 5,},Spokes: 20, // NOTE: trailing comma necessary here (and at Radius) }
JSON
序列化
Go中的序列化叫做编组,使用
json.Marshal
函数:package mainimport ("encoding/json""fmt""log" )type Movie struct {Title stringYear int `json:"released"` // 反引号括起来是结构体成员标签Color bool `json:"color,omitempty"`Actors []string }var movies = []Movie{{Title: "Casablanca", Year: 1942, Color: false, Actors: []string{"Humph Boke", "Inge ZZ"}},{Title: "Casablanca", Year: 1942, Color: true, Actors: []string{"Humph Boke", "Inge ZZ"}}, }func main() {data, err := json.Marshal(movies)if err != nil {log.Fatalf("JSON marshaling failed: %s", err)}fmt.Printf("%s\n", data) }
使用上边函数结果是没有空白缩进的:
[{"Title":"Casablanca","released":1942,"Actors":["Humph Boke","Inge ZZ"]},{"Title":"Casa blanca","released":1942,"color":true,"Actors":["Humph Boke","Inge ZZ"]}]
使用
json.MarshalIndent
函数将产生整齐缩进的输出:data, err := json.MarshalIndent(movies, "*", " ") // 参数:解析切片,前缀,缩进 if err != nil {log.Fatalf("JSON marshaling failed: %s", err) } fmt.Printf("%s\n", data)
结果:
[ * { * "Title": "Casablanca", * "released": 1942, * "Actors": [ * "Humph Boke", * "Inge ZZ" * ] * }, * { * "Title": "Casablanca", * "released": 1942, * "color": true, * "Actors": [ * "Humph Boke", * "Inge ZZ" * ] * } *]
Go复合数据类型学习总结相关推荐
- 《Go语言圣经》学习笔记 第四章 复合数据类型
<Go语言圣经>学习笔记 第四章 复合数据类型 目录 数组 Slice Map 结构体 JSON 文本和HTML模板 注:学习<Go语言圣经>笔记,PDF点击下载,建议看书. ...
- 学习C++复合数据类型
复合数据类型 String 类型 char str[20]; //strlen(str)的长度不一定是20 cin.getline(str,20); //读入一行输入(cin是iostream类的一个 ...
- 五、Go语言复合数据类型(下)
@Author:Runsen 复合数据类型主要包含Go中的一些复杂类型,主要有指针类型,数组类型,切片类型,结构体类型,Map类型和Channel类型 下面,我们继续学习结构体类型,Map类型和Cha ...
- 春晚黑科技全盘点:刘德华、周杰伦到底来没来?Ubuntu linux c复合数据类型--结构体
本文转载自IT之家,刘德华.周杰伦到底有没有来春晚? 很多看了 2021 年央视春晚的人会对这个问题感兴趣--其实,周杰伦.刘德华是通过 AR.云技术 "出现"在现场的. 央视春晚 ...
- golang json转结构体中嵌套多个数组_ElasticSearch第六篇:复合数据类型-数组,对象...
在ElasticSearch中,使用JSON结构来存储数据,一个Key/Value对是JSON的一个字段,而Value可以是基础数据类型,也可以是数组,文档(也叫对象),或文档数组,因此,每个JSON ...
- 【oracle】复合数据类型
[oracle]复合数据类型 1123-02 更新 复合数据类型 复合数据类型举例 1.record set serveroutput on; /* 复合数据类型--记录:for循环中 给 记录类型变 ...
- PL/SQL-2 复合数据类型
-------------------------------------------------------------------------- ************************* ...
- PL/SQL复合数据类型
--一.PL/SQL复合数据类型 --(一).PL/SQL记录 --1.定义PL/SQL记录 --(1).定义PL/SQL记录 --Grammar TYPE type_name IS RECORD(f ...
- java中的复合数据类型是什么_【填空题】类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素。一个类的实现包括两部分:____和_____....
[填空题]类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素.一个类的实现包括两部分:____和_____. 更多相关问题 [名词解释] 观叶树木 [单选] 开花时有浓郁香气的树种是 ...
最新文章
- Python使用matplotlib可视化小提琴图、seaborn中的violinplot函数可视化多分类变量的小提琴图(Violin Plot)
- ERR_CONTENT_LENGTH_MISMATCH
- java 正则匹配 sql星号,18. 正则表达式:开头、结尾、任意一个字符、星号和加号匹配...
- Python IDLE 如何清屏
- linux驱动之I2C
- VS2010 + OpenCV 2.4.1 环境配置
- 错误./hello: error while loading shared libraries: libQtGui.so.4: cannot open shared object file:
- 【Elasticsearch】es 定期删除 已经删除的数据 物理删除 不是等待段合并
- 4.13_chain_of_responsibility_行为型模式:责任链模式
- caffe利用shell创建train.txt和val.txt做数据输入
- 分享 那些经典电影的经典台词
- 洛谷P3239 [HNOI2015]亚瑟王
- spaCy 2.1 中文NLP模型
- pytorch(7)——二十二种transforms数据预处理方法
- dymola学习笔记第三天——胡言乱语篇
- minSdk(API 29) deviceSdk(API 127)
- win7下ie6兼容测试之Windows7(win7)下 XP Mode 下载、安装、设置完全图解
- 网页代码基本结构以及html标签的使用
- java分页查询_java实现分页查询
- 微信小程序如何打开网页
热门文章
- 虚拟机服务器桥接网络配置,虚拟机Vmware下CentOS6.5配置Bridged桥接方式上网及远程登录...
- 育儿品牌“亲宝宝”获数亿元C轮融资,好未来领投,顺为、复星跟投
- 造轮子之Vue实现原理,几十行代码实现Vue
- KEIL 创建静态链接库+ 调用自己创建的静态链接库
- Python学习13-15.1-15.12 保持时间、计划任务和启动程序
- std::map emplace和insert使用
- 漫步STL-map AND set
- windows不是正版电脑壁纸变黑色
- uboot 引导wince NK.nb0
- 数迹智能 VisionChina(上海)2020 展精彩回顾