1 数组
# 1 数组是类似于数字、字符串、 的基础数据类型
# 2 数组是同一类型元素的集合
eg:整数集合:5,8,9,79,76 形成一个数组
Go--数组 : 不允许混合不同类型的元素 eg: 包含字符串和整数的数组
python--列表 : 可以放多个任意类型的元素
# 3 数组是连续存储的内存空间,一旦定义,不能扩大或缩小
# 注:
1.python中没有数组,元组 与 数组 不是同一个东西
package main
import "fmt"
//数组
func main() {
//1 定义数组,不赋初值 数组元素的值为零值
var a [4]int8 //定义了一个大小为4的int类型 a 数组,最多放4个值,并且必须都是int类型
fmt.Println(a) //[0 0 0 0]
var b [3]string
fmt.Println(b)
//2 赋值和取值 从0开始 没有负数 没有步长
var a [4]int8
a[0] = 99
fmt.Println(a[0]) //99
fmt.Println(a) //[99 0 0 0]
//3 定义并赋初值
//var a [4]int8 = [4]int8{1, 2, 3, 4}
var a = [4]int8{1, 2, 3, 4} // 数组定义,必须固定大小
a := [4]int8{1, 2, 3, 4}
a := [...]int8{1, 2, 3, 4} //能看懂即可,一般不用
//看到...写法,但是实际大小也是固定的,就是定义赋值时的大小
fmt.Println(a)
a :=[100]int8{98:99} //只把第99位设置为99,其他为零值
fmt.Println(a[98])
//4 数组大小也是类型的一部分
var a = [4]int8{1, 2, 3, 4}
var b = [3]int8{1, 2, 3}
// a和b就不是同一个类型
test1(b) // test1函数 就不能传递 a,b 两个变量进去,因为类型不一样
//5 数组是值类型,不是引用类型
当参数传递到函数中,传递的是复制的值,而不是引用地址,不会修改掉原来的数据
go语言中的函数传递参数,都是copy传递,把参数复制一份传过去
var a = [3]int{2: 66}
test1(a) // 99,88,66
fmt.Println(a) // 0,0,66
//6 数组长度
var a = [30]int{2: 66}
fmt.Println(len(a))
//7 循环数组 索引循环与迭代循环
var a = [3]int{2: 66}
//7.1 基于索引的循环
for i := 0; i <len(a); i++ {
fmt.Println(a[i])
}
//7.2 基于迭代的循环 for + range range后放,key被迭代的东西
range是关键字,并不是内置函数
for i := range a{ //如果使用给一个值接收,就是索引
fmt.Println(v)
}
//for _, v := range a{ //只需要其中一个,借助'_'
//for i, _ := range a{
for i, v := range a{ //如果使用两个值接收,就是索引和值
fmt.Println(i)
fmt.Println(v)
}
//8 多维数组
//长度为3,类型为[4]int的数组 二维数组
var a [3][4]int
fmt.Println(a) //[[0 0 0 0] [0 0 0 0] [0 0 0 0]]
var a =[3][4]int{{1,1,1,1},{},{5,5,5}}
fmt.Println(a) //[[1 1 1 1] [0 0 0 0] [5 5 5 0]]
fmt.Println(a[2][2]) //5
// 循环二维数组 ---> 两层for循环 几维数组,就几层for循环
}
func test1(a [3]int) {
a[0]=99
a[1]=88
fmt.Println(a)
}
2 切片(slice)
# 切片是由 数组 建立的一种方便、灵活且功能强大的包装(Wrapper)
# 切片本身不拥有任何数据,它们只是对现有数组的引用。
# 注:
1.非常常用的数据结构
2.切片是引用类型
package main
import "fmt"
// 切片的使用
func main() {
//1 切片的定义
// 1.1 方式一:
//先定义数组
var a [10]int
//再从数组上切出切片 []后 数组里的类型
var s []int=a[:] //冒号前后写 数组的索引数字 前闭后开
var s = a[:] //采用类型推导
fmt.Println("切片s是:",s) //ln指的是换行的意思
fmt.Printf("s的类型是:%T\n",s) //s的类型是:[]int --> 切片
fmt.Println("数组a是:",a)
fmt.Printf("a的类型是:%T\n",a) //a的类型是:[10]int --> 数组
// 1.2 方式二:定义且赋值
var s []int=[]int{6, 7, 8}
var s =[]int{6, 7, 8} // 类型推导
//2 因为切片的对底层数组的引用,故若改变了切片,底层数组会变 若改变了数组,切片也会变
fmt.Println("---------------------------")
s[0]=99 //切片的修改和取值,也是根据索引
fmt.Println("切片s是:",s)
fmt.Println("数组a是:",a)
fmt.Println("---------------------------")
a[1]=88
fmt.Println("数组a是:",a)
fmt.Println("切片s是:",s)
//3 不从头切到尾,只切一部分 切片和数组都会同时修改
var a [10]int
var s []int=a[5:]
fmt.Println(s)
s[0]=100
fmt.Println(s) // [100 0 0 0 0]
fmt.Println(a) // [0 0 0 0 0 100 0 0 0 0]
a[9]=999
fmt.Println(s) // [100 0 0 0 999]
//4 切片的长度和容量
var a [10]int
var s []int=a[5:]
fmt.Println(len(s)) //长度 5
fmt.Println(cap(s)) //容量(它最大能存多少值) 5
var a [10]int
var s []int=a[5:7] //前闭后开
fmt.Println(s)
fmt.Println(len(s)) //长度 2
fmt.Println(cap(s)) //容量(它最大能存多少值) 5
虽然此时长度为2,但原数组切片的后面还有3个
//5 切片的追加值
var a [10]int
var s []int=a[5:7]
//内置函数append
s=append(s,3)
fmt.Println(s) //[0 0 3]
fmt.Println(len(s)) //长度 3
fmt.Println(cap(s)) //容量 5
//追加到最大长度
s=append(s,3,4,5) //返回值还是切片类型
fmt.Println(s) //[0 0 3 4 5]
fmt.Println(len(s)) //长度 5
fmt.Println(cap(s)) //容量 5
fmt.Println(a) //[0 0 0 0 0 0 0 3 4 5]
//再追加一个
s=append(s,6)
fmt.Println(s) //[0 0 3 4 5 6]
fmt.Println(len(s)) //长度 6
fmt.Println(cap(s)) //容量 10
如果不够了,翻倍--原来容量的2倍(在1024以内)
fmt.Println(a) //[0 0 0 0 0 0 0 3 4 5]
//此时该切片已经脱离原来的数组了,自己新造了一个数组,指向了新数组,并且把值copy了过去
//现在改数组,不会影响切片;改切片,也不会影响数组
a[9]=999
s[2]=888
fmt.Println(a) //[0 0 0 0 0 0 0 3 4 999]
fmt.Println(s) //[0 0 888 4 5 6]
//6 只定义切片 不赋值,不知道长度和容量 引用类型的零值都是nil
var s []int //零值: nil ---> python的None、java的null
fmt.Println(s) //[]
if s== nil{
fmt.Println("我是空的")
}else {
fmt.Println("我不是空的")
}
//7 通过make内置函数 创建切片
第一个参数是类型,第二个参数是长度,第三个参数是容量
var s []int= make([]int,3,4)
//var s []int= make([]int,3) //第三个参数容量不写,同长度一样
fmt.Println(s) //[0 0 0] 为何不是[],因为知道长度为3,类型为int 零值就是0
s[0]=99
fmt.Println(s) //[99 0 0]
fmt.Println(len(s)) //长度 3
fmt.Println(cap(s)) //容量 4
//8 切片是引用类型 当做参数传递时,修改值 会不会影响原来的切片?
注意:前提取决于是否追加 超过到最大容量:
若没有超过,修改值 --> 会影响原来的数组,从而影响原来的切片
若超过, 修改值 --> 会创建新的数组,不会影响原来的数组 从而不影响原来的切片
var s []int= make([]int,3,4)
//go 函数参数传递都是copy传递,而s是引用类型,传递的是新复制出来的一个切片引用
//eg1: 不追加,只修改值 会影响原来的切片
test1(s)
fmt.Println(s) //[999 0 0]
//eg2: 追加但没超,修改值 会影响原来的切片
test2(s)
fmt.Println(s) //[999 0 0]
//eg2: 追加且超过,修改值 不会影响原来的切片
test3(s)
fmt.Println(s) //[0 0 0]
//8 切片是引用类型 当做参数传递时,在函数内部追加值 会不会影响? 不会影响原来的切片
只追加值,根据是否超过容量,有可能会影响原来的数组 但不会影响原来的切片
若只是增加了数组,不修改数组值,原来的切片不受影响
var s []int= make([]int,3,4)
test4(s)
fmt.Println(s) //[999 0 0]
//9 切片底层存储
type slice struct {
Length int //长度
Capacity int //容量
ZerothElement *byte //指向数组的某个位置
}
//10 多维切片 二维切片 注:内层切片也要初始化,否则是nil
//10.1 定义
//初始化了第一层切片
var s [][]int=make([][]int,3,4)
fmt.Println(s) //[[] [] []]
fmt.Println(s[0]==nil) //true 切片的零值为nil
//迭代循环:初始化第二层切片
for i:=range s{
s[i]=make([]int,3,4)
}
//索引循环:初始化第二层切片
for i := 0; i < len(s); i++ {
s[i]=make([]int,3,4)
}
fmt.Println(s) //[[0 0 0] [0 0 0] [0 0 0]]
//10.2 定义且赋值
var s [][]int=[][]int{{1,2,},{3,4,5,6,7,8},{6,7,8,},{8,7,7,7,7}}
fmt.Println(len(s)) //4
fmt.Println(cap(s)) //4
fmt.Println(len(s[1])) //6
fmt.Println(cap(s[1])) //6
//11 内存优化 copy函数
只要切片在内存中,数组就不能被垃圾回收
1w个byte--->1024个byte--->1k--->10k
//var a [10000]int8=[10000]int8{0:11,1:22,2:33}
var a [10000]int8=[10000]int8{11,22,33}
var s1 []int8=a[:3]
fmt.Println(s1) //使用s1时,一直使用的a数组,该数组比较大 但切片只用小部分
var s2 []int8=make([]int8,2,2)
fmt.Println(s2) //使用s2时,是基于新创建的小数组
copy(s2,s1) //把s1切片copy给s2切片
如果新切片 长于 旧切片 新切片后面用各类型的零值补齐
如果新切片 短于 旧切片 以新切片的长度为准
fmt.Println(s2)
}
func test1(a []int) {
a[0]=999
fmt.Println(a) //[999 0 0]
}
func test2(a []int) {
a=append(a,888) //追加但没超
a[0]=999
fmt.Println(a) //[999 0 0 888]
}
func test3(a []int) {
a=append(a,888,999) //追加且超过
a[0]=999
fmt.Println(a) //[999 0 0 888 999]
}
func test4(a []int) {
a[0]=999
a=append(a,888,777,666) //追加超过最大容量,切片引用指向新的数组了
a[1]=222
fmt.Println(a) //[999 222 0 888 777 666]
}
3 可变长参数
//可变长参数 就是切片类型
package main
import "fmt"
func main() {
test(1,2,3,4,5)
//可直接传切片,需要加 ... '...' 类似于把切片中的元素 打散传递进去
var s []int=[]int{1,2,3,4,5}
test(s...)
}
func test(a ...int) {
fmt.Println(a) //a就是一个切片
fmt.Println(a[0]) //1
fmt.Printf("%T",a) //[]int
}
4 字典(maps)
# maps 是在 Go 中将值(value)与键(key)关联的内置类型
通过相应的键可以获取到值
其实就key:value这种结构,类似于python字典
package main
import "fmt"
// maps
func main() {
//1 定义map: map[key类型]value类型
var m map[int]string //零值是nil -->引用类型
fmt.Println(m) //map[] 其实就nil
fmt.Println(m == nil) //true
//2 定义并初始化
//var m map[int]string=make(map[int]string,3) //不要传数字,数字:底层数组的长度
var m map[int]string=make(map[int]string)
fmt.Println(m) //是空map map[]
fmt.Println(m==nil) //false
//3 定义并初始化,放入值
var m map[int]string=map[int]string{1:"lqz",2:"xxx",9:""}
//4 赋值、修改值
//var m map[int]string //如果不初始化,nil赋值会报错
var m map[int]string=make(map[int]string) // 初始化了不报错
m[1]="lqz" //如果没有初始化,赋值报错
m[1]="egon"
m[2]="xxx" //如果存在就修改,不存在,就新增
fmt.Println(m) //map[1:egon 2:xxx]
//5 取值
var m map[int]string=make(map[int]string)
m[1]="egon"
m[2]="xxx"
fmt.Println(m[1]) //egon
fmt.Println(m[9]) //不报错,返回value值的零值
//6 判断一个key是否在map中
var m map[int]string=map[int]string{1:"lqz",2:"xxx"} //定义并初始化,放入值
v,ok :=m[2] //取值可以返回两个
如果2个变量来接收,值 和 布尔(存在就是true,不存在就是false)
如果1个变量来接收,值
fmt.Println(v) //xxx
fmt.Println(ok) //true
var m map[int]string=map[int]string{1:"lqz",2:"xxx"}
if _,ok:=m[9];ok{
fmt.Println("值在")
}else {
fmt.Println("值不在")
}
//7 删除元素
var m map[int]string=map[int]string{1:"lqz",2:"xxx"}
delete(m,1)
fmt.Println(m) //map[2:xxx]
//8 长度
//var m map[int]string=map[int]string{1:"lqz",2:"xxx"} //肉眼可见 长度为2
var m map[int]string=make(map[int]string,5) //数字: 底层数组的长度 一般不写
不写,默认会有一个固定长度
若你能预计map的长度,可以写上
但若是超过该长度,内部就会存在数组扩容 反而导致资源浪费
fmt.Println(len(m))
fmt.Println(cap(m)) //报错,map没有容量的概念
//9 Map 是引用类型
//var m map[string]string=map[string]string{1:"lqz",2:"xxx"}
m :=map[string]string{"name":"lqz","age":"18"}
test(m)
fmt.Println(m) //map[name:egon, age:18]
//10 Map 的相等性
map之间不能使用 '==' 操作符判断, '=='只能用来检查map是否为 nil
map1 := map[string]int{"one": 1, "two": 2}
map2 := map1
if map1 == map2 { //报错
}
//11 循环
m :=map[string]string{"name":"lqz","age":"18"}
for k,v:=range m{
fmt.Println(k)
fmt.Println(v)
}
//12 map的存储是无序的 ---> hash类型本来就是无序的
python3.6 将字典以后做成有序了,本质是内部加了一个列表,来表示顺序位置
//13 map底层存储,都是以数组形式存储
map、字典 ---> 统统称为hash类型,对key值执行hash
//var m map[string]string
1.新定义map时,底层会创建出 默认长度(或指定数字的那个长度)的数组
//m["name"] = "lqz"
2.在对map赋值时
---> 会将map的key值,进行hash,得到一个数字
---> 再将该数字,对数组长度 取余
---> 余数 一定会落在数组的长度范围内
---> 最后,在数组[余数的位置上],存储value值
3.在对map取值时 //时间复杂度 O(1) 一次就能命中
---> 会将map的key值,进行hash,得到一个数字
---> 再将该数字,对数组长度 取余
---> 余数 一定会落在数组的长度范围内
---> 最后,在数组[余数的位置上],获取value值
4.可能造成hash冲突 //在更多的值进行存储时,hash取余后,余数可能相同,进行存放时 就会冲突
//14 解决hash冲突的方法
下面方法在数据够多时,会额外增加查找的时间复杂度
故语言内部都会有一个监测因子,达到一定时,会自动扩容底层数组
1.再哈希法 //最简单
采用新的hash方法函数,再hash一次,得到一个新地址放进去
2.链地址法 //Java hashmap就是这么做的
如果冲突了,直接使用 链表 直接挂载当前位置下面
--->为减少时间复杂度,又使用 平衡二叉树、红黑树 进行挂载
3.开放定址法 //python 字典使用这种
在产生冲突时,就直接向后找一个空的单元来存放关键字
-线性探测再散列
-二次探测再散列
-伪随机探测再散列
4.建立一个公共溢出区
当溢出发生时,将所有溢出数据统一放到溢出区
}
func test(m map[string]string) {
m["name"]="egon"
fmt.Println(m) //map[name:egon, age:18]
}
标签:03,maps,int,fmt,var,切片,Println,map,Go
From: https://www.cnblogs.com/Edmondhui/p/17142680.html