Golang_5_数据
目录:
五、数据
5.1 字符串
-
默认值为
""
而不是nil
-
换行字符串使用 `,前置缩进空格也属于字符串内容
func main() {
s := `line\r\n,
line 2`
fmt.Println(s)
}
// 输出:
// line\r\n,
// line 2
- 支持字符串间的比较
- 允许以索引号访问字节数组,但不能获取元素地址
- 分配超大字符串 ==92==
5.2 数组
-
定义方式:
func main() { var a [4]int b := [4]int{2, 5} // 未初始化的元素默认为0 c := [4]int{5, 3: 10} // 指定索引位置初始化 d := [...]int{1, 2, 3} // 编辑器按照初始化值数量确定数组长度 e := [...]int{10, 3: 100} // e 长度为 4,因为设定了索引位置 fmt.Println(a, b, c, d, e, len(e)) }
-
元素类型相同,长度也相同时才属于同一类型,才能直接赋值
- 复制和传参操作都会复制整个数组
func main() { a := [4]int{1, 2, 3, 4} b := [4]int{5, 6, 7, 8} a = b for i := range a { fmt.Println(a[i]) // 输出 5678 } }
func test(x *[2]int) { fmt.Printf("x: %p, %v\n", x, *x) x[1] += 100 } func main() { a := [2]int{10, 20} test(&a) fmt.Printf("x: %p, %v\n", &a, a) // x: 0xc420016090, [10 20] // x: 0xc420016090, [10 120] }
-
多维数组
- 只有第一纬度允许使用
b := [...][2]int{}
- 内置函数 len 和 cap 只返回第一维度的长度
- 只有第一纬度允许使用
5.3 切片
切片本身并非动态数组或数组指针。内部通过指针引用底层数组,设定相关属性将数据读写操作限定在指定区域内。
切片本身是个只读对象,工作机制类似数组指针的一种包装
func main() {
x := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(x[:], len(x[:])) // 0 到 最后
fmt.Println(x[2:5], len(x[2:5])) // 2 到 4 (前面取到后面取不到)
fmt.Println(x[2:5:7], len(x[2:5:7]), cap(x[2:5:7])) // 2 开始 ,最小到 5,最大到 7,len是可读的写元素数量,cap表示切片所引用长度的真实长度
fmt.Println(x[:5:7], len(x[:5:7]), cap(x[:5:7]))// 0开始
}
len是可读的写元素数量,cap表示切片所引用长度的真实长度
直接创建切片对象,使用make或显示初始化语句
func main() {
s1 := make([]int, 3, 5) // 指定 len、cap
s2 := make([]int, 3) // 省略 cap,和 len 相等
s3 := []int{10, 20, 5: 30} // 初始化元素分配底层数组,并设置 len、cap
fmt.Println(s1, len(s1), cap(s1))
fmt.Println(s2, len(s2), cap(s2))
fmt.Println(s3, len(s3), cap(s3))
}
- 不支持比较操作,只能判断是否为
nil
- 可以获取元素地址,但不能直接用指针访问元素内容
make
支持运行期间动态指定数组长度
reslice
以切片为数据源,再此基础创建切片
切片中的修改操作对所有关联切片可见
func main() {
d := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := d[3:7]
fmt.Println(s1) // [3 4 5 6]
s2 := s1[1:3]
fmt.Println(s2) // [4 5]
fmt.Println("修改后:")
for i := range s2 {
s2[i] += 100
}
fmt.Println(d) // [0 1 2 3 104 105 6 7 8 9]
fmt.Println(s1) // [3 104 105 6]
fmt.Println(s2) // [104 105]
}
reslice实现一个简单的栈
func main() {
stack := make([]int, 0, 5)
push := func(x int) error {
n := len(stack)
if n == cap(stack) {
return errors.New("stack is full")
}
stack = stack[:n+1]
stack[n] = x
return nil
}
pop := func() (int, error){
n := len(stack)
if n == 0 {
return 0, errors.New("stack is empty")
}
x := stack[n-1]
stack = stack[:n-1]
return x, nil
}
for i := 0; i < 7; i++ {
fmt.Printf("push %d: %v, %v\n", 100*i, push(100*i), stack)
}
for i := 0; i < 7; i++ {
x, err := pop()
fmt.Printf("pop: %d, %v, %v\n", x, err, stack)
}
}
append
向切片尾部追加数据,返回新的切片对象
func main() {
s := make([]int, 0, 5)
s1 := append(s, 10)
s2 := append(s1, 20, 30)
fmt.Println(s, len(s), cap(s)) // [] 0 5
fmt.Println(s1, len(s1), cap(s1)) // [10] 1 5
fmt.Println(s2, len(s2), cap(s2)) // [10 20 30] 3 5
}
追加过程若超出cap限制,则会对新切片重新分配数组
func main() {
s := make([]int, 0, 100)
s1 := s[:2:4]
s2 := append(s1, 1, 2, 3, 4, 5, 6)
fmt.Printf("s1 - %p - %v\n", &s1[0], s1)
fmt.Printf("s2 - %p - %v (超过限制的切片)\n", &s2[0], s2)
fmt.Printf("s - %p - %v\n",&s1[0], s[:10])
fmt.Printf("s_cap: %d, s1_cap: %d, s2_cap: %d\n",cap(s), cap(s1), cap(s2))
}
// 输出如下:
// s1 - 0xc420086000 - [0 0]
// s2 - 0xc420018100 - [0 0 1 2 3 4 5 6] (超过限制的切片) 重新开辟了空间
// s - 0xc420086000 - [0 0 0 0 0 0 0 0 0 0] 对超限的切片操作,原切片不受影响
// s_cap: 100, s1_cap: 4, s2_cap: 8
copy
两个切片之间复制数据,运行指向同一底层数组,允许目标区间重叠。最终所复制长度以较短的切片长度(len)为准
copy(s1, s2)
:s1 对应位置用 s2 来替换
例1:
s1 := []int{0, 1, 2, 3, 4, 5, 6}
s2 := []int{9, 9, 9}
n := copy(s1, s2)
n
返回一个int
长度,这时为 3copy
后,s1
变为{9, 9, 9, 3, 4, 5, 6}
copy(s2, s1)
后s2
变为{1, 2, 3}
例二:
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := s[5:8]
n := copy(s[4:], s1)
fmt.Println("copy(s[4:], s1)之后的s1:", s1)
fmt.Println( "n:", n, " s:",s)
s2 := make([]int, 6)
n = copy(s2, s)
fmt.Println("n:", n, "s2:", s2)
}
输出:
s1: [5 6 7]
copy(s[4:], s1)之后s1: [6 7 7]
n: 3 s: [0 1 2 3 5 6 7 7 8 9]
n: 6 s2: [0 1 2 3 5 6]
- s1 之操作前为 [5 6 7]
copy(s[4:], s1)
翻译为 s 的 4 之后用 5, 6, 7 代替- s 变为 [0 1 2 3 5 6 7 7 8 9]
- 由于 s1 取自于 s,所以变为[6, 7, 7]
copy(s2, s)
翻译为 s2 从头开始由 s 对应位置填充
5.4 字典
无序键值对集合,引用类型,使用 make
或 初始化语句 创建,内部使用散列表Hash
实现
使用
定义
make(map[KeyType]ValueType, [cap])
cap是map的容量,非必须,但是最好指定
基本使用
func main() {
scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap) // map[张三:90 小明:100]
fmt.Println(scoreMap["小明"]) // 100
fmt.Println(scoreMap["小红"]) // 0 默认值为0
fmt.Printf("type of a: %T\n", scoreMap) // type of a: map[string]int
}
也可以在声明的时候填充元素
func main() {
userInfo := map[string]string{
"username": "admin",
"password": "123",
}
fmt.Println(userInfo) // map[username:admin password:123]
}
判断某个键是否存在
v, ok := scoreMap["张三"]
如果key存在ok为true, v为对应的值;
不存在ok为false, v为值类型的零值
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
v, ok := scoreMap["张三"]
if ok {
fmt.Println(v) // 90
} else {
fmt.Println("查无此人")
}
v2, ok2 := scoreMap["张三2"]
fmt.Println(ok2, v2) // false 0
}
遍历
使用 for range
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["小红"] = 90
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
也可以只遍历key
for k := range scoreMap {
fmt.Println(k)
}
删除键值对
delete(scoreMap, "小明")
清空map的操作如下:
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["小红"] = 90
scoreMap = make(map[string]int)
按照指定顺序遍历
第24行开始,根据key的顺序遍历map
func main() {
rand.Seed(time.Now().UnixNano()) // 时间种子
var scoreMap = make(map[string]int, 200)
// 初始化 map
// map[stu64:66 stu84:67 stu44:41 stu45:56 ... ]
for i := 0; i < 100 ; i++ {
key := fmt.Sprintf("stu%02d", i) // 生成 stu 开头的字符串
value := rand.Intn(100) // 生成 0~99 的随机数
scoreMap[key] = value
}
// 取出map中所有key存入切片keys
// [stu64 stu84 stu44 stu45 ... ]
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
// 排序切片
sort.Strings(keys)
// 按照排序后的 key 遍历 map
for x, key := range keys {
fmt.Println(x, key, scoreMap[key])
// 0 stu00 61
// 1 stu01 48
// 2 stu02 13
// ...
}
}
元素为 map类型 的切片
func main() {
var mapSlice = make([]map[string]string, 3)
for index, value := range mapSlice {
fmt.Printf("index: %d, value: %v\n", index, value)
}
fmt.Println("after init")
// [map[] map[] map[]] 外层是切片,内层是 map
// 下面第一行必加,因为要定义切片内 map 的类型
mapSlice[0] = make(map[string]string, 10)
mapSlice[0]["name"] = "小王子"
mapSlice[0]["password"] = "123"
mapSlice[0]["address"] = "CN"
fmt.Println(mapSlice)
// [map[name:小王子 password:123 address:CN] map[] map[]]
for index, value := range mapSlice {
fmt.Printf("index: %d, value: %v\n", index, value)
}
// index: 0, value: map[password:123 name:小王子 address:CN]
// index: 1, value: map[]
// index: 2, value: map[]
}
值为切片的map
func main() {
// key 为 string,值value 为string类型的切片
var sliceMap = make(map[string] []string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
// map[]
// after init
// map[中国:[北京 上海]]
5.5 结构体
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- 字段名:表示结构体字段名。结构体中的字段名必须唯一。
- 字段类型:表示结构体字段的具体类型。
实例化结构体
type person struct {
name string
city string
age int8
}
func main(){
var p1 person
p1.name = "Aris"
fmt.Printf("p1=%#v\n", p1)
}
匿名结构体
临时数据场景使用
func main() {
var user struct{Name string; Age int}
user.Name = "Aris"
user.Age = 21
fmt.Printf("%#v\n", user)
}
指针类型结构体
- 使用
new(person)
创建指针结构体 - 使用
&person{}
创建指针结构体 - 支持结构体指针使用
.
访问结构体成员
func main() {
var p = new(Person)
p2 := &Person{}
p.Name = "Aris"
fmt.Printf("p=%#v\n", p) // p=&main.Person{Name:"Aris", Age:0}
}
结构体初始化
键值对
可以只初始化部分字段,其余字段为零值(string
零值为""
)
p1 := Person{
Name: "Aris",
City: "AL",
Age: 18,
}
p2 := &Person{
Name: "Aris-2",
}
fmt.Printf("%#v", p2)
// &main.Person{Name:"Aris-2", City:"", Age:0}
只用值
需要 按顺序 对 所有字段 进行初始化,不能和键值对方法混用
p3 := &Person{
"Aris-3",
"BJ",
0,
}
==结构体内存布局==
结构体占用一块连续的内存