文章目录
一. 数组
- 数组
长度不可变
- 所有元素类型相同
- 重复赋值,只能是
长度相同的数组
- 元素,为非负证书的常量
- 声明
var
,需正数定:类型,长度
%q
默认会有双引号,会显示其数值类型%v
调用函数自己的格式
1. 声明,获取
var nums [10]int
// 不声明长度,有多少元素,长度就为多少
var nums [...]int{1,2,3,4,5}
//通过索引赋值
nums :=[10]int{1:2,8:10}
简易声明
nums :=[3]int{1,2,3}
2. 判断,遍历
相等 A == B
长度 len(A)
索引 nums[0] , nums[10]=20
,
遍历数组
for range
遍历,不需要索引获取
nums :=[10]int{0,1,2,3,4,5,6,7,8,9}
//索引 index , 字符value
for index,value := range nums{
fmt.Println(index,"\t:",value)
}
//index可以变为_
for _,value := range nums{
fmt.Println(value)
}
3. 切片
切片生成的类型为 [ ]int
,并不是数组,第三位为切片的容量
长度
,不能大于数组的容量 左包含,又不包含
容量
3. 多维数组
不能求长度
声明
var nums [3][2]int
赋值
nums[0]=[2]int{1,2}
//索引声明
nums[0][0]=10
遍历
for range
4. (符合数组map)映射 ,字典
map 也叫做符合数组
,hash表
,字典
,一堆 未 排序的键值对集合
- 是一个
无序
的key/value存储, key
只能使用运算符的数值类型(字符串,数字,布尔,数组)value
为任意类型,slice,map等- 可以使用
==
和!=
来进行判等操作,换句话说就是key
必须是可哈希的。(你的 key 不能是切片,不能是字典,不能是函数) - 未初始化的 score 的零值为nil,无法直接进行赋值(
要使用 make 函数先对其初始化
)–也就是初始化定义如果没有直接赋值,需要用make进行声明,才能够赋值
map声明语法
var 变量名 map[keytype]valuetype
map类型的变量默认初始值为nil
,需要使用make()函数来分配内存
。语法为
make(map[KeyType]ValueType, [cap])
var sources map[string]int
//字面量
sources =map[string]int{"老大":30,"老二":28,"老三":25}
fmt.Println(sources)
sources=make(map[string]int)
或者
m1 := map[string]string{
"坦克": "德玛西亚",
"射手": "伊泽瑞尔",
"辅助": "日女",
}
查
长度len(source)
key
fmt.Println(sources["老二"])
v,ok :=sources["老二"]
map查找值
//v值,ok判断是否存在
val, ok := m1["k4"]
if ok {
fmt.Printf("k4的值是:%v\n", val)
}
初始化,通过ok去判断—为true执行子语句
如果 key 不存在,也不报错,会返回其value-type 的零值
改
修改value
sources["老大"]=40
如果修改key,需要删除,然后添加
增:如果key不存在就是添加(随意插入
),如果不存在(静默处理)
sources["猪八戒"]=1000
删
delete(sources,"老三")
遍历—无序的
for k,v :=range sources{
fmt.Println(k,v)
}
判断 key 是否存在
当key不存在,会返回value-type的零值
,所以你不能通过返回的结果是否是零
值来判断对应的 key 是否存在,因为 key 对应的 value 值可能恰好就是零值。
其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key 是否存在,若存在ok为true,若不存在,则ok为false
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
math, ok := scores["math"]
if ok {
fmt.Printf("math 的值是: %d", math)
} else {
fmt.Println("math 不存在")
}
}
我们将上面的代码再优化一下
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
if math, ok := scores["math"]; ok {
fmt.Printf("math 的值是: %d", math)
} else {
fmt.Println("math 不存在")
}
}
如何对字典进行循环
Go 语言中没有提供类似 Python 的 keys() 和 values() 这样方便的函数,想要获取,你得自己循环。
循环还分三种
1. 获取 key 和 value
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for subject, score := range scores {
fmt.Printf("key: %s, value: %d\n", subject, score)
}
}
2. 只获取key,这里注意不用占用符。
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for subject := range scores {
fmt.Printf("key: %s\n", subject)
}
}
3. 只获取 value,用一个占位符替代。
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for _, score := range scores {
fmt.Printf("value: %d\n", score)
}
}
多元的
定义一个映射,在他的基础上也映射
相当于他的value也是一个映射
#注意这里的value类型为string,也就是说必须带双引号
var user map[string]map[string]string
user =map[string]map[string]string{"李明":{"中国":"陕西","年纪":"大二","成绩":"99","练习方式":"123412421"}}
fmt.Printf("%T\n,%#v\n",user,user)
赋值,相当于多元数组一样
users["李明"]["成绩"]=“100”
排序
相当于对索引排序
m := map[string]string{"q": "q", "w": "w", "e": "e", "r": "r", "t": "t", "y": "y"}
fmt.Println(m)
//定义一个 string类型切片
var slice []string
//循环遍历map,取出所有的key和value
for k, _ := range m {
//循环将key添加到切片中
slice = append(slice, k)
}
fmt.Printf("切片slice值 : %v\n", slice)
//调用排序包,对切片进行排序,按照字母顺序排序
sort.Strings(slice[:])
fmt.Printf("排序后 切片slice值 : %v\n", slice)
for _, v := range slice {
fmt.Printf("排序后 m[%v]=%v\n", v, m[v])
}
投票选举问题
优化:没有直接加一
获取每个字符出现的次数
遍历字符串key结果为麻点
,rune
类型
所以value为int类型
二. 切片 Slice
切片是可动态变化
的序列,是对数组的引用,引用类型,遵循引用传递的机制
用的是数组
进行存储
- 一个
长度可变
的数组,存储地址在内存中 - 只能存储
类型相同
的元素 - slice由三个部分构成,
指针、长度、容量
- 类型为
[]int
指针:指向切片第一个元素指向数组的地址
长度:切片元素的数量
容量:切片开始到结束位置的数量 - 切片也可以进行切片(
获取新的切片
) - 左包含,
右不包含
缺点:
声明 : :不需要指定长度的数组
var name []string
var num []int
数组切片赋值
获取长度len()
,
获取容量cap()
- 未定义的时候长度,容量相等
fmt.Printf("slice第一个元素地址%p\n",&s1[0])
fmt.Printf("对应数组元素的地址%p\n",&months[4])
切片的创建方式make
定义切片,然后引用已经创建好的数组,数组可见
内置make函数创建切片
,底层数组看不见,只能通过slice访问元素
make创建切片内存分配图
cap
取到最后一位
,多了不要
,
要检查切片是否为空,使用len(s) == 0
来判断
new()和make()的区别
看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。
new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T
的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体
;它相当于 &T{}。
make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:切片、map 和 channel
1. 定义切片的容量(make)
make函数:指定长度,容量
len指定长度,打印几个元素
内置make函数,make(类型,len,cap)
,注意cap>=
len,容量可以省略,默认等于长度
类型 len
nums =make([]int,3)
容量为5
nums =make([]int,3,5)
切片直接对应数组,如同make方式
var s1 []int = []int{1, 2, 3, 4, 5}
fmt.Println(s1)
fmt.Println(len(s1))
fmt.Println(cap(s1))
遍历切片
var arr [5]int = [...]int{11, 22, 33, 44, 55}
s1 := arr[1:4]
//for循环遍历
for i := 0; i < len(s1); i++ {
fmt.Printf("s1[%v]=%v\n", i, s1[i])
}
fmt.Println()
//for range方式遍历切片
for i, v := range s1 {
fmt.Printf("索引i=%v 值v=%v\n", i, v)
}
2 . 切片(增,删,改,查)
左包含,右不包
含
- len()
- Cap 统计切片容量,最大存放多少元素
//创建切片
var slice1 []int = []int{100, 200, 300}
fmt.Printf("slice1容量=%v 长度=%v\n", cap(slice1), len(slice1))
- append
- copy
查–用索引
fmt.Println(nums[0])
修改
nums[0]=10
追加::添加一个元素,增加的是长度,容量不变
如果超出长度,长度增加,容量会成倍增加
需要返回值
更新
- append原理就是
对底层数组扩容
,go会创建新的数组,将原本元素拷贝到新的数组中
切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍
。
// 追加一个切片
, … 表示解包
,不能省略
myarr = append(myarr, []int{7, 8})
// 在第一个位置
插入元素
myarr = append([]int{0}, myarr)
// 在中间插入一个切片
(两个元素)
myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]))
如果是要将一个切片
追加到另一个切片
尾部,需要使用 ...
语法将第2个参数展开为参数列表
a := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
// a == []string{"John", "Paul", "George", "Ringo", "Pete"}
个参数展开为参数列表。
2. slice
重新引用新的数组
3. 这个数组不可见
nums=append(nums,1)
nums= append(nums,1,2,3)
复制:copy(目的,源)
不会改变
现有大小
A :=[]int{9.8.7}
B :=[]int{1,2,3,4,5}
copy(B,A)
//结果B ={9.8.7.4.5}
copy :函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准。两个 slice 可指向同一底层数组,允许元素区间重叠。
package main
import (
"fmt"
)
func main() {
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("array data : ", data)
s1 := data[8:]
s2 := data[:5]
fmt.Printf("slice s1 : %v\n", s1)
fmt.Printf("slice s2 : %v\n", s2)
copy(s2, s1)
fmt.Printf("copied slice s1 : %v\n", s1)
fmt.Printf("copied slice s2 : %v\n", s2)
fmt.Println("last array data : ", data)
}
输出结果:
array data : [0 1 2 3 4 5 6 7 8 9]
slice s1 : [8 9]
slice s2 : [0 1 2 3 4]
copied slice s1 : [8 9]
copied slice s2 : [8 9 2 3 4]
last array data : [8 9 2 3 4 5 6 7 8 9]
删除
删除第一个
nums[1:]
删除最后一个
nums[:len(nums)-1]
删除中间的:相当于将后面的前移,然后缩小(中间几个元素)
//删除2:5
copy(num[2:],num[5:])
num[:len(num)-3]
func main() {
// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
fmt.Println(a) //[30 31 33 34 35 36 37]
}
调整大小:resize
package main
import (
"fmt"
)
func main() {
var a = []int{1, 3, 4, 5}
fmt.Printf("slice a : %v , len(a) : %v\n", a, len(a))
b := a[1:2]
fmt.Printf("slice b : %v , len(b) : %v\n", b, len(b))
c := b[0:3]
fmt.Printf("slice c : %v , len(c) : %v\n", c, len(c))
}
输出结果:
slice a : [1 3 4 5] , len(a) : 4
slice b : [3] , len(b) : 1
slice c : [3 4 5] , len(c) : 3
思考题
- 超出原 slice.cap 限制,就会重新分配底层数组,即便原数
组并未填满。
package main
import (
"fmt"
)
func main() {
data := [...]int{0, 1, 2, 3, 4, 10: 0}
s := data[:2:3]
s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。
fmt.Println(s, data) // 重新分配底层数组,与原数组无关。
fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。
}
输出结果:
[0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
0xc4200160f0 0xc420070060
- 为什么
package main
import (
"fmt"
)
func main() {
var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
myslice := numbers4[4:6:8]
fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice))
myslice = myslice[:cap(myslice)]
fmt.Printf("myslice的第四个元素为: %d", myslice[3])
}
结果
myslice为 [5 6], 其长度为: 2
myslice的第四个元素为: 8
结论:
三. 队列(先进先出)
添加元素:追加方式
删除元素:队头
添加元素
queue :=[]int
queue =append(queue,1)
queue =append(queue,2)
queue =append(queue,3)
删除元素
queue = queue[1:]
四. 堆栈(先进后出)
stack
移除元素,从对尾移除
num = num[:len(num)-1]
五. 多维切片
points :=[][]int{}
// [][]int 类型,0长度
num :=make([][]int,0)
points=append(points,[]int{1,2,3})
points=append(points,[]int{4,5,6})
fmt.Println(points)
访问
point[0][0]
六. 模块
地址https://golang.google.cn/pkg/
例如:
sort
排序模块 Ints() , Strings()
等
七 . 字符串/ 字节切片
string底层就是byte数组,因此string同样可以进行切片处理
1. 字符串 常用函数
区别
bytes·:将buffer对象转化为字节切片
string`:将buffer对象转换为字符串
转换:可以将string通过bute[] 转换为字节切片,同时也可以转换为string
常用包
string包中用UTF-8
- 在本地查看文档cmd—
godoc --help
,访问本地localhost:6060
- 也可以用
go doc strings
查看go doc string.Fields
官方文档
Compare 比较字符串 错误-1
Contains 是否包含子字符串
Count 子字符串出现的次数
EqualFold 不区分大小写比较
Fields 按空白字符分割字符串(\n,空格,\t,\r,\f,\v)
HasPrefix 是否以字符串 开头
HasSuffix 是否以字符串 结尾
Index 获取字符串首次出现的次数
Split 分割字符串为切片,以某个字符串为分隔符
Join 将字符串切片连接strings.Join([]string{"abc","def"},":")
LastIndex 获取子字符串最后一次出现的位置
Repeat 把字符串重复几次
Replace 把字符串替换
ToLower 把字符串转换为小写
ToUpper 把字符串转换为大写
ToTitle 把字符串转换为大写
package main
import (
"fmt"
"strings"
)
func main() {
//比较
fmt.Println(strings.Compare("abc", "cbd"))
//包含
fmt.Println(strings.Contains("abcd", "cd"))
//子字符串出现的次数
fmt.Println(strings.Count("abcdabababab", "ab"))
fmt.Println(strings.EqualFold("ABCD","abcd"))
fmt.Println(strings.Fields("ab cd\nef\rgh\flm"))
fmt.Println(strings.HasPrefix("abcd","a"))
fmt.Println(strings.HasSuffix("abcd","d"))
fmt.Println(strings.Index("abcadaeafg","f"))
fmt.Println(strings.Split("abcdefg" ,"e"))
fmt.Println(strings.Join([]string{"abc","def"},":"))
fmt.Println(strings.LastIndex("abcdabababab","ab"))
//重复几次
fmt.Println(strings.Repeat("abc",3))
//替换ab为gg,替换3次
fmt.Println(strings.Replace("abcdefgabacdab","ab","gg",3))
}
2. 字节 常用函数
定义字节切片
var bytes []byte= []byte{'a','b','c'}
将字节
转换 为字符串
s :=string(bytes)
fmt.Printf("%T,%v\n",s,s)
将字符串转换为字节
by := []bytes(s)
fmt.Printf("%T,%v\n",s,s)
函数
类似于string
计算长度:用utf8
包
中文,一个占三个字节,但用utf8就为一个字符
精确计算 字符串,计算utf8
字符长度
如果是汉字字符串(一个汉字等于三个字节)
要计算正确的长度utf8
s :="我爱洗澡"
m :=utf8.RuneCountInString(s)
//结果为 4
fmt.Println(m)
八. 字符串转换( String包 )
strconv
包
1. 字符串到其他类型
用的是Prese
- bool
用stconv.ParsBool()
//v值,err判断是否为nil
if v,err := strconv.ParseBool("true");err==nil{
fmt.Println(v)
}
- int类型
Atoi直接转化为int
if v,err :=strconv.Atoi("1234");err==nil{
fmt.Println(v)
}
PresInt
需要指定多少进制的,转换为多少字节
//把64,为16进制,转换为64字节
//16*6+4=100
if v,err :=strconv.ParseInt("64",16,64);err==nil{
fmt.Println(v)
}else {
fmt.Println(err)
}
- float类型
if v,err :=strconv.ParseFloat("1.1111",64);err==nil{
fmt.Println(v)
}
2. 其他类型到字符串
fmt
包
Sprint
将你的变量转换为字符串
3. 如何修改字符串
强制
将string转化为[]byte,修改后转为string
[]byte只能处理英文和数字
,不能处理汉字,汉字3个字节,会出现乱码
arr1 := []byte(str1) //类型强转
arr1[0] = 'g'
str1 = string(arr1)
fmt.Printf("str1=%v\n", str1)
- 将string转为[]rune指针类型,按字符处理,兼容
汉字
arr2 := []rune(str1)
arr2[0] = '糕'
str1 = string(arr2)
fmt.Printf("str1=%v\n", str1)
例如:
package main
import (
"fmt"
"strings"
)
func main() {
str := "hello world"
//判断是不是以某个字符串开头,返回布尔值
res0 := strings.HasPrefix(str, "http://")
res1 := strings.HasPrefix(str, "hello")
fmt.Printf("res0 is %v\n", res0)
fmt.Printf("res1 is %v\n", res1)
//判断是不是以某个字符串结尾
res3 := strings.HasSuffix(str, "http://")
res4 := strings.HasSuffix(str, "world")
fmt.Printf("res3 is %v\n", res3)
fmt.Printf("res4 is %v\n", res4)
//判断字符在字符串中首次出现的索引位置,没有返回-1
res5 := strings.Index(str, "o")
res6 := strings.Index(str, "x")
fmt.Printf("res5 is %v\n", res5)
fmt.Printf("res6 is %v\n", res6)
//返回字符最后一次出现的索引位置,没有返回-1
res7 := strings.LastIndex(str, "o")
res8 := strings.LastIndex(str, "x")
fmt.Printf("res7 is %v\n", res7)
fmt.Printf("res8 is %v\n", res8)
//字符串替换
res9 := strings.Replace(str, "world", "golang", 2)
res10 := strings.Replace(str, "world", "golang", 1)
//trings.Replace("原字符串", "被替换的内容", "替换的内容", 替换次数)
//原字符串中有2个world,才能替换2次
fmt.Printf("res9 is %v\n", res9)
fmt.Printf("res10 is %v\n", res10)
//求字符在字符串中出现的次数,不存在返回0次
countTime0 := strings.Count(str, "h")
countTime1 := strings.Count(str, "x")
fmt.Printf("countTime0 is %v\n", countTime0)
fmt.Printf("countTime1 is %v\n", countTime1)
//重复几次字符串
res11 := strings.Repeat(str, 0)
res12 := strings.Repeat(str, 1)
res13 := strings.Repeat(str, 2)
// strings.Repeat("原字符串", 重复次数)
fmt.Printf("res11 is %v\n", res11)
fmt.Printf("res12 is %v\n", res12)
fmt.Printf("res13 is %v\n", res13)
//字符串改大写
res14 := strings.ToUpper(str)
fmt.Printf("res14 is %v\n", res14)
//字符串改小写
res15 := strings.ToLower(str)
fmt.Printf("res15 is %v\n", res15)
//去除首尾的空格
res16 := strings.TrimSpace(str)
fmt.Printf("res16 is %v\n", res16)
//去除首尾指定的字符,遍历l、d、e然后去除
res17 := strings.Trim(str, "ld")
fmt.Printf("res17 is %v\n", res17)
//去除开头指定的字符
res18 := strings.TrimLeft(str, "he")
fmt.Printf("res18 is %v\n", res18)
//去除结尾指定的字符,遍历d、l、r
res19 := strings.TrimRight(str, "dlr")
fmt.Printf("res19 is %v\n", res19)
//用指定的字符串将string类型的切片元素结合
str1 := []string{"hello", "world", "hello", "golang"}
res20 := strings.Join(str1, "+")
fmt.Printf("res20 is %v\n", res20)
}
注意
切片:传递的是地址,修改A,B也会改变
数组:是值传递,修改B,A地址中的内容不变
-
Go 中数组赋值和函数传参都是值
复制
的。 -
切片传数组参数,既可以达到节约内存的目的,也可以达到合理处理好共享内存的问题。
-
切片是引用传递,所以它们不需要使用额外的内存并且比使用数组更有效率