Array
数组
-
- var a [5]int b:=[5]int{} c:=[...]int{}这样格式定义
- var a[5]int 和var a[10]int是不同类型
- 从0开始下标,到len(a)-1
- 遍历方式:
for i := 0; i < len(a); i++ {
}
for index, v := range a {
}
-
- 注意越界问题,panic
- 值类型,传参时会拷贝产生副本,不是指针 (因此会有性能问题)可以考虑传指针或者slice等引用类型
- 指针数组 [n]*T 数组指针 *[n]T。
简单demo
package main
import "fmt"
// 定义数组测试
func Test1() {
var a [5]int
b := [...]float64{1, 2, 3}
//cap和len都会输出数组长度
lenA := cap(a)
lenB := len(b)
fmt.Println(a, lenA)
fmt.Println(b, lenB)
//[0 0 0 0 0] 5
//[1 2 3] 3
}
// 循环输出测试
func PrintArr(arr [5]int) {
//对比地址可以发现是拷贝的值
fmt.Println(&arr[0])
//循环输出元素测试
for i := 0; i < len(arr); i++ {
fmt.Println(i, "->", arr[i])
}
fmt.Println("***********************")
for index, value := range arr {
fmt.Println(index, "->", value)
}
}
func main() {
//Test1()
arr := [5]int{1, 2, 3, 4}
fmt.Println(&arr[0])
PrintArr(arr)
}
Slice
切片
最常用,最重要类型之一
变长序列,引用了数组对象,灵活
指针(底层数组,但不一定数组首地址),长度len,容量cap
初始化demo
package main
import "fmt"
func main() {
//var arr1 [5]int
//声明切片,注意与数组对比
var s1 []int
if s1 == nil {
fmt.Println("初始化为空!")
} else {
fmt.Println("不为空!")
}
fmt.Println(s1)
// :=
s2 := []int{}
fmt.Println(s2)
// make方式 len(2) cap(3)
var s3 []int = make([]int, 2, 3)
fmt.Println(s3, "len(s3)=", len(s3), " cap(s3)=", cap(s3))
//len(4) cap(4)
s4 := make([]int, 4)
fmt.Println(s4, "len(s4)=", len(s4), " cap(s4)=", cap(s4))
//从数组中切片
arr := [5]int{1, 2, 3, 4, 5}
//左[ 右)
s6 := arr[1:4]
fmt.Println(s6)
}
简单理解slice底层demo
具体源码在src/runtime/slice.go当中
package main
import "fmt"
func main() {
arr := [6]int{0, 1, 2, 3, 4, 5}
x := arr[:6]
y := arr[1:3]
fmt.Println(arr)
fmt.Println(x, y)
// [0 1 2 3 4 5]
// [0 1 2 3 4 5] [1 2]
fmt.Println()
arr[1] = 100
fmt.Println(arr)
//实际操作底层数组
fmt.Println(x, y)
// [0 100 2 3 4 5]
// [0 100 2 3 4 5] [100 2]
fmt.Println()
x[2] = 200
fmt.Println(arr)
//实际操作底层数组
fmt.Println(x, y)
}
追加元素append demo
共享内存下容易出现的bug,请仔细分析
老数组
初始化时若:newSlice := array[1:2:2] ,如果第三个值(cap(slice))大于len(slice)
cap>len的时候请注意共享内存下的数据。
package main
import "fmt"
// 切片len<cap 注意会影响共享内存下的值
func main() {
array := [4]int{10, 20, 30, 40}
slice := array[0:2]
newSlice := append(slice, 300)
//这两个很有区别,这个扩容超出cap进行重新分配
//newSlice := append(slice, 300, 400, 500)
fmt.Println(slice, newSlice)
fmt.Printf("&slice=%p &newSlice=%p\n", &slice, &newSlice)
fmt.Printf("&slice[0]=%p &newSlice[0]=%p\n", &slice[0], &newSlice[0])
fmt.Println(slice, newSlice, array)
newSlice[1] = 200
fmt.Printf("&slice=%p &newSlice=%p\n", &slice, &newSlice)
fmt.Printf("&slice[0]=%p &newSlice[0]=%p\n", &slice[0], &newSlice[0])
fmt.Println(slice, newSlice, array)
}
新数组:
扩容策略,即内存不够的时候重新分配array数组
当cap比较小的时候,扩容直接2倍
随着增大就扩容1/3,1/4等等逐渐减少
新数组扩容demo
package main
import "fmt"
// 了解分配策略
func main() {
//初始化slice
slice := []int{0}
c := cap(slice)
num := 0 //计数,扩容次数
for i := 0; i < 1024; i++ {
//添加前cap
preArr := &slice[0]
preSlice := &slice
slice = append(slice, i)
//添加后cap变大了就代表扩容了
if n := cap(slice); n > c {
fmt.Println("cap:", c, " --> ", n)
fmt.Printf(" 扩容前后底层array地址:%p --> %p\n", preArr, &slice[0])
fmt.Printf(" 扩容前后slice地址:%p --> %p\n\n", preSlice, &slice)
c = n
num++
}
}
fmt.Println("扩容了", num, "次!")
}
总结:对于slice append造成扩容时,其slice的地址&slice不变,变化的是其内部array的地址
Slice详解分析:
https://halfrost.com/go_slice/
删除切片某元素demo
go语言并没有删除切片元素的接口,因此只能根据特性删除,[:]
删除开头元素:
//方法1:移动array的指针
s := []int{1, 2, 3, 4}
fmt.Printf("%#v\n", s)
//fmt.Println(unsafe.Sizeof(int(0)))
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
//删除前两个元素,把底层的ptr向右移动
s = s[2:]
fmt.Printf("%#v\n", s)
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
//方法2:不改变array指针,但是代价是后面array数据向前移动
s := []int{1, 2, 3, 4}
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
//删除掉a[0]
s = append(s[:0], s[1:]...)
fmt.Printf("%#v\n", s)
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
//方法3:copy函数辅助
s := []int{1, 2, 3, 4}
fmt.Printf("%#v\n", s)
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
//删除掉a[0] a[1]
s = s[:copy(s, s[2:])]
fmt.Printf("%#v\n", s)
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
删除中间元素
s := []int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Printf("%#v\n", s)
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
//删除a[3]
//s = append(s[:3], s[4:]...)
//s = s[:3+copy(s[3:], s[4:])] // 删除中间1个元素
//删除a[3]之后的3个元素
//s = append(s[:3], s[6:]...)
s = s[:3+copy(s[3:], s[6:])] // 删除中间1个元素
fmt.Printf("%#v\n", s)
fmt.Printf("&s = %p\n", &s)
fmt.Printf("&s[0] = %p\n\n", &s[0])
删除尾部元素
a = []int{1, 2, 3}
a = a[:len(a)-1] // 删除尾部1个元素
a = a[:len(a)-N] // 删除尾部N个元素
切片删除元素:Go语言从切片中删除元素
container/list
这个是container中的list使用,也常见,了解即可。
package main
import (
"container/list"
"fmt"
)
func main() {
var mylist = list.List{}
mylist.PushBack(123)
mylist.PushBack(456)
mylist.PushBack(789)
fmt.Println(mylist)
// 遍历list
for i := mylist.Front(); i != nil; i = i.Next() {
fmt.Println(i.Value)
}
fmt.Println("--------------")
for i := mylist.Back(); i != nil; i = i.Prev() {
fmt.Println(i.Value)
}
// 其他需求,自己查阅资料
}
https://juejin.cn/post/6888117219213967368#heading-2
Map
哈希表,无序key/value键值对
map[keyType][valueType] ,key是唯一的,其对应的value也唯一
map必须初始化才能读写,否则只能读
线程不安全,在多线程当中使用sync.Map,我编辑在了并发编程当中
简单demo
package main
import "fmt"
// 全局初始化
var map1 map[int]string
func main() {
//等价
//可以声明时初始化
//scoreMap := map[string]int{}
//可以声明初始化空间
scoreMap := make(map[string]int, 0)
scoreMap["张三"] = 100
scoreMap["赵六"] = 10
scoreMap["李四"]=60
// 双参数返回,val,ok,如果单参数val,!ok返回空,有可能key是空
if value, ok := scoreMap["张三"]; ok {
fmt.Println(value)
} else {
fmt.Println("查无此人")
}
fmt.Println(scoreMap)
fmt.Printf("%#v\n", scoreMap)
// 删除key,value
delete(scoreMap, "张三")
fmt.Println(scoreMap)
fmt.Printf("%#v\n", scoreMap)
}
输出是没有顺序的,如果想要按照一定顺序,可以先取出key,然后排序,之后输出。
key对应一个value,其中如果value切片的话会很灵活,map类型的切片也很灵活
对map进行打印
var courseMap = map[string]string{
"go": "go语言圣经",
"gin": "深入理解gin框架",
"grpc": "grpc理解",
}
for key, value := range courseMap {
fmt.Println(key, "-->", value)
}
fmt.Println("---------------")
for key := range courseMap {
fmt.Println(key, "-->", courseMap[key])
}