文章目录
数组
- 数组的空间大小写在类型前;
- 除常规遍历数组方法之外,还可以通过range关键字进行遍历;
- 可通过 _ 省略变量,不仅仅在range中,任何地方都可以通过 _ 省略变量;
- 数组是值类型,调用 func f(arr [10] int)会拷贝数组;
- 数组是作为函数参数时是值类型,即拷贝;其他语言中获取数组名即获取数组的首元素地址,是引用传递;
- [10] int 和[20] int是不同的数据类型;
- Go中一般不直接使用数组。
- 数组定义
- 数组定义的案例
var arr1 [5]int // 这里仅仅定义为对数组初始化,但go给数组每个元素赋值默认值0
arr2 := [3]int{1, 3, 5} // 使用:=定义时必须进行初始化
arr3 := [...]int{2, 4, 6, 8, 10} // 数组欺骗
var grid [4][5]bool // 多维数组
fmt.Println(arr1, arr2, arr3)
fmt.Println(grid)
输出结果:
[0 0 0 0 0] [1 3 5] [2 4 6 8 10]
[[false false false false false] [false false false false false] [false false false false false] [false false false false false]]
- 数组遍历
- 数组遍历的案例
// 遍历方法一 使用range关键字
for i := range arr3 { // 关键字range获得数组arr3的下标
fmt.Println(arr3[i])
}
//关于range另外写法
for i, v := range arr3 {
// 关键字range同时获得数组arr3的下标和对应的元素值
// 若不想写变量i,可以用_替代i
fmt.Println(i, v) // 输出下标以及元素值
}
// 遍历方法二
for i := 0; i < len(arr2); i++ {
fmt.Println(arr2[i])
}
- 数组参数传递
数组参数传递的案例
// 值传递
func printArray1(arr [5]int){
arr[0] = 100 // 验证数组是值传递而非引用传递
for i, v := range arr {
fmt.Println(i, v)
}
}
// 引用传递,借用指针
func printArray2(arr *[5]int){
arr[0] = 100 // 验证数组是值传递而非引用传递
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int // 这里仅仅定义为对数组初始化,但go给数组每个元素赋值默认值0
arr2 := [3]int{1, 3, 5} // 使用:=定义时必须进行初始化
arr3 := [...]int{2, 4, 6, 8, 10}
fmt.Println("--------------值传递")
fmt.Println("printArray1(arr1)")
printArray1(arr1) // 可行
fmt.Println("printArray1(arr3)")
printArray1(arr3) // 可行
// printArray1(arr2) // 不可行
fmt.Println("arr1 and arr3")
fmt.Println(arr1, arr3)
fmt.Println("-------------引用传递")
fmt.Println("printArray2(&arr1)")
printArray2(&arr1) // 取地址
fmt.Println("printArray2(&arr3)")
printArray2(&arr3)
fmt.Println("arr1 and arr3")
fmt.Println(arr1, arr3) // 数组的第一个元素被改为100
}
输出结果:
--------------值传递
printArray_value(arr1)
0 100
1 0
2 0
3 0
4 0
printArray_value(arr3)
0 100
1 4
2 6
3 8
4 10
arr1 and arr3
[0 0 0 0 0] [2 4 6 8 10] // 第一个元素没有变成100
-------------引用传递
printArray_reference(&arr1)
0 100
1 0
2 0
3 0
4 0
printArray_reference(&arr3)
0 100
1 4
2 6
3 8
4 10
arr1 and arr3
[100 0 0 0 0] [100 4 6 8 10] // 第一个元素变成100
注
为什么要使用range
- 意义明确,美观
- c++:没有类似能力
- Java/Python:只能for each value,不能同时获取i, v
切片(Slice)
- 切片的定义
-
slice本身没有数据,是对底层array的一个view;
-
slice是引用类型,是数组array的引用,是动态数组;
-
切片的定义的案例
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println("arr[2:6] = ", arr[2:6]) // slice是引用类型,是数组的引用,是动态数组
fmt.Println("arr[:6] = ", arr[:6])
fmt.Println("arr[2:] = ", arr[2:])
fmt.Println("arr[:] = ", arr[:]) // arr[:] 和 arr不同;arr[:]是切片,动态数组,而arr仅仅是数组
}
输出结果:
arr[2:6] = [2 3 4 5]
arr[:6] = [0 1 2 3 4 5]
arr[2:] = [2 3 4 5 6 7 8]
arr[:] = [0 1 2 3 4 5 6 7 8]
- 切片的引用传递
- 切片的引用传递的案例
func updateSlice(s []int) { // 不带长度的,表示Slice
s[0] = 100
}
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println("-------slice")
s1 := arr[2:]
fmt.Println("s1 : ", s1)
s2 := arr[:]
fmt.Println("s2 : ", s2)
// 切片是引用传递的
fmt.Println("After updateSlice(s1)")
updateSlice(s1)
fmt.Println(s1)
fmt.Println(arr)
fmt.Println("After updateSlice(s2)")
updateSlice(s2)
fmt.Println(s2)
fmt.Println(arr)
}
输出结果:
-------slice
s1 : [2 3 4 5 6 7 8]
s2 : [0 1 2 3 4 5 6 7 8]
After updateSlice(s1)
[100 3 4 5 6 7 8]
[0 1 100 3 4 5 6 7 8]
After updateSlice(s2)
[100 1 100 3 4 5 6 7 8]
[100 1 100 3 4 5 6 7 8]
- 切片自切
- 切片自切的案例
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
s2 := arr[:]
fmt.Println("Reslice") // 自切
fmt.Println(s2)
s2 = s2[:5]
fmt.Println(s2)
s2 = s2[2:]
fmt.Println(s2)
}
输出结果
Reslice
[0 1 2 3 4 5 6 7 8]
[0 1 2 3 4]
[2 3 4]
- 切片扩展
-
slice可以向后扩展,不可以向前扩展;
-
切片的长度len是它所包含的元素个数。
-
切片的容量cap是从它的第一个元素开始数,到其底层数组元素末尾的个数
-
s[i]不可以超越len(s),向后扩展可以超越len(s)但不可以超越底层数组cap(s),如下例;
-
切片扩展的案例
func main(){
fmt.Println("Extending slice") // slice 的扩展
arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
s1 = arr1[2:6] // s1中有4个元素,但切片扩展的元素的个数为4+3(隐藏的元素个数)=7个
s2 = s1[3:6] // [ s1[3], s1[4], s1[5] ] 向后扩展了
// var _ int = s1[4] // 报错,数组下标越界 runtime error: index out of range [4] with length 4
// 切片能读取后面隐藏的元素即扩展元素,数组不能读取到隐藏的元素
fmt.Printf("arr1 = %v, len(arr1) = %d\n", arr1, len(arr1))
fmt.Printf("s1 = %v, len(s1) = %d, cap(s1) = %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2 = %v, len(s2) = %d, cap(s2) = %d\n", s2, len(s2), cap(s2))
}
输出结果:
Extending slice
arr1 = [0 1 2 3 4 5 6 7 8], len(arr1) = 9
s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 7
s2 = [5 6 7], len(s2) = 3, cap(s2) = 4
- Slice的创建
-
创建方法一:同定义未初始化的数组一样但不写明长度
-
创建方法二:同边定义边初始化的数组一样但不写明长度
-
创建方法三:使用 make() 定义Slice
-
创建Slice的案例
func printSlices(s []int) {
fmt.Printf("%v, len = %d, cap = %d\n",
s, len(s), cap(s))
}
func main() {
fmt.Println("Create Slice")
// 创建方法一
var s []int // Zero value for slice is nil
// 此时 s == nill
for i := 0; i < 100; i++{
printSlices(s)
s = append(s, 2 * i + 1)
}
fmt.Println(s)
fmt.Println("--------------")
// 创建方法二
s1 := []int{2, 4, 6, 8} // len = 4, cap = 4
printSlices(s1)
// 创建方法三
s2 := make([]int, 15) // len = 15, cap = 15
s3 := make([]int, 10, 32) // len = 10, cap = 32
// s2, s3为初始化,都赋值默认值0
printSlices(s2)
printSlices(s3)
}
部分输出结果:
...
--------------
[2 4 6 8], len = 4, cap = 4
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], len = 15, cap = 15
[0 0 0 0 0 0 0 0 0 0], len = 10, cap = 32
- 向Slice添加元素
-
添加元素时如果超越cap,系统会重新分配更大的底层数组;
-
又要值传递的关系,必须接受append的返回值;
-
s = append(s, val)
-
添加元素
func main(){
arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
s1 = arr1[2:6]
s2 = s1[3:6]
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println("s3, s4, s5 = ", s3, s4, s5)
// s4 and s5 no longer view arr1
fmt.Println("arr1 = ", arr1)
}
输出结果:
append slice
s3, s4, s5 = [5 6 7 10] [5 6 7 10 11] [5 6 7 10 11 12]
arr1 = [0 1 2 3 4 5 6 7 10]
// 其中s4, s5的底层数组不知道在哪里
注
向Slice进行append操作时,可能需要更换新底层数组,从而导致Slice的ptr,len,cap都变更,需要新的Slice来接受这三个值;
- Slice的复制
-
通过系统内建函数 copy() 来实现。
-
Slice复制的案例
func main(){
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 15)
printSlices(s1)
printSlices(s2)
fmt.Println("Copying slice")
copy(s2, s1) // 将s1复制到s2上
printSlices(s2)
}
输出结果:
[2 4 6 8], len = 4, cap = 4
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], len = 15, cap = 15
Copying slice
[2 4 6 8 0 0 0 0 0 0 0 0 0 0 0], len = 15, cap = 15
- Slice元素的删除
-
Go中无内建删除函数;
-
利用append()函数进行删除操作;
-
s = append(s[:index], s[index+1:]…);
-
Slice元素的删除的案例
func main(){
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 15)
copy(s2, s1)
printSlices(s2)
fmt.Println("Deleting elements from slice")
// 删除下标为3的元素
s2 = append(s2[:3], s2[4:]...) // append的第二个参数是可变参数
printSlices(s2)
// 删除切片首元素
fmt.Println("Popping from front")
font := s2[0]
s2 = s2[1:]
fmt.Println("front = ", font)
printSlices(s2)
// 删除切片尾元素
fmt.Println("Popping from back")
back := s2[len(s2) - 1]
s2 = s2[:len(s2) - 1]
fmt.Println("back = ",back)
printSlices(s2)
}
输出结果:
[2 4 6 8 0 0 0 0 0 0 0 0 0 0 0], len = 15, cap = 15
Deleting elements from slice
[2 4 6 0 0 0 0 0 0 0 0 0 0 0], len = 14, cap = 15
Popping from front
front = 2
[4 6 0 0 0 0 0 0 0 0 0 0 0], len = 13, cap = 14
Popping from back
back = 0
[4 6 0 0 0 0 0 0 0 0 0 0], len = 12, cap = 14
注
cap表示是slice中ptr到底部数组最后一个元素之间元素个数
利用append删除中间元素时,底部数组并没有改变,故cap=15,由于少一个元素,长度len=14;
删除slice的首元素,需要slice的ptr向后移动一位,故cap=14,由于少一个元素,长度len=13;
删除slice的尾元素,没有影响ptr,以及底部数组不变,故cap=14,由于少一个元素,长度len=12;
上述过程中s2的物理地址没有发送变化
键值对(Map)
- Map的结构:map[K]V,键为K 值为V;
- Map的复合结构:map[K1]map[K2]V, 外面的map的 键为K1 值为map,里面的map的 键为K2 值为V;
- Map创建
- 创建方式一:map [ 键类型 ] 值类型 { “键名” : “值” }
- 创建方式二:make ( map [ 键类型 ] 值类型 )
- 创建方法三:var Map名 map [ 键类型 ] 值类型
- 创建案例
func main(){
// map构建方法一
m := map[string]string {
"name" : "ccmouse",
"course" : "Golang",
"sit" : "immoc",
"quality" : "notbad",
}
fmt.Println(m)
// map构建方法二
m2 := make(map[string]int) // 空的,未初始化 m2 == empty
fmt.Println(m2)
// map构建方法三
var m3 map[string]int // m3 == nil
fmt.Println(m3)
}
输出结果:
map[course:Golang name:ccmouse quality:notbad sit:immoc]
map[]
map[]
- Map的遍历
- 使用range遍历key,或者遍历key,value对;
- 不保证遍历顺序,如需顺序,需要手动对key排序;
- map中的元素是无序的, 每次遍历顺序是不一样;
- 使用len获得map的元素个数;
- map使用哈希表,必须可以比较相等;
- 除了slice,map,function的内建类型都可以作为key;
- 自定义类型Struct不包含上述字段,也可作为key;
- 遍历案例
func main(){
m := map[string]string {
"name" : "ccmouse",
"course" : "Golang",
"sit" : "immoc",
"quality" : "notbad",
}
// map遍历
fmt.Println("------Traversing map------")
for k, v := range m {
fmt.Println(k, v)
}
// 只要k
fmt.Println("------Only need K------")
for k := range m {
fmt.Println(k)
}
// 只要v
fmt.Println("------Only need V------")
for _, v := range m {
fmt.Println(v)
}
// 每次遍历顺序不一样,map中的元素是无序的
}
输出结果:
------Traversing map------
name ccmouse
course Golang
sit immoc
quality notbad
------Only need K------
sit
quality
name
course
------Only need V------
ccmouse
Golang
immoc
notbad
- 获得Map中的元素
- 获取元素:m [ key ]
- key不存在时,获得Value类型的初始值
- 通过键获取对应值的案例
func main(){
m := map[string]string {
"name" : "ccmouse",
"course" : "Golang",
"sit" : "immoc",
"quality" : "notbad",
}
fmt.Println("------Getting values------")
courseName := m["course"]
fmt.Println(courseName)
causeName := m["cause"] // m中不存在cause键
fmt.Println(causeName) // 获取不存在键对应的值时,输出结果是空的,这里是空串
}
输出结果:
------Getting values------
Golang
- 用value, ok := m [ key ] 来判断是否存在key
- 判断元素是否存在map的案例
func main(){
m := map[string]string {
"name" : "ccmouse",
"course" : "Golang",
"sit" : "immoc",
"quality" : "notbad",
}
// 判断k是否在map中
if caseName, ok := m["case"]; ok { // m中不存在case
fmt.Println(caseName)
}else {
fmt.Println("key does not exist")
}
}
输出结果:
key does not exist
- 删除map中的元素
func main(){
m := map[string]string {
"name" : "ccmouse",
"course" : "Golang",
"sit" : "immoc",
"quality" : "notbad",
}
// 删除map中的元素,使用内建函数delete()
fmt.Println("------Deleting values------")
name, ok := m["name"]
fmt.Println(name, ok)
delete(m, "name")
name, ok = m["name"]
fmt.Println(name, ok)
}
输出结果:
------Deleting values------
ccmouse true
false
字符和字符串处理
- 使用range遍历pos,rune对;
- 使用utf8.RuneCountInString获得字符数量;
- len(string)只是获得字节数
- 使用[]byte获得字节;
- byte转为rune就不需要考虑UTF-8编码问题;
- 字符串处理案例
func main() {
s := "Yes我爱慕课网!"
fmt.Println(len(s))
for _, b := range []byte(s) {
fmt.Printf("%X ", b)
}
fmt.Println()
// UTF-8 可变长
//一个英文占一个字节,一个中文占三个字节
for i, ch := range s { // ch is a rune 四字节的int型(int32)
// UTF-8转为Unicode
fmt.Printf("(%d, %X) ", i, ch)
}
fmt.Println()
fmt.Println("Rune count:",utf8.RuneCountInString(s))
bytes := []byte(s)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes) // 英文字符的size是1,中文的字符size是3
bytes = bytes[size:]
fmt.Printf("%c ", ch)
}
fmt.Println()
// 将字符串转rune
for i, ch := range []rune(s) {
// 重新开了一个rune数组
fmt.Printf("(%d %c) ", i, ch)
}
fmt.Println()
}
输出结果:
19
59 65 73 E6 88 91 E7 88 B1 E6 85 95 E8 AF BE E7 BD 91 21
(0, 59) (1, 65) (2, 73) (3, 6211) (6, 7231) (9, 6155) (12, 8BFE) (15, 7F51) (18, 21)
Rune count: 9
Y e s 我 爱 慕 课 网 !
(0 Y) (1 e) (2 s) (3 我) (4 爱) (5 慕) (6 课) (7 网) (8 !)
strings包中的各种操作方法;
Fields,Split,Join
- Fields 空格分割
func Fields (s string) []string
- 返回将字符串按照空格(连续的空格被认为一个空格进行分割)分割的多个字符串。如果字符串全部是空格或者是空字符串的话,会返回空切片。
- example
fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
- output
Fields are: ["foo" "bar" "baz"]
- Split 以sep为分割符分割
func Split(s, sep string) []string
-
以指定的分隔符拆分,返回一组切片;
-
用去掉s中出现的sep的方式进行分割,会分割到结尾,并返回生成的所有片段组成的切片(每一个sep都会进行一次切割,即使两个sep相邻,也会进行两次切割)。如果sep为空字符,Split会将s切分成每一个unicode码值一个字符串。
-
example
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q\n", strings.Split(" xyz ", ""))
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
- output
["a" "b" "c"]
["" "man " "plan " "canal panama"]
[" " "x" "y" "z" " "]
[""]
- Join连接
func Join(a []string, sep string) string
- 将一系列字符串连接为一个字符串,之间用sep来分隔;
- example
s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
- output
foo, bar, baz
Contains,Index
- Contains判断是否包含子串
func Contains(s, substr string) bool
- 判断字符串s是否包含子串substr。
- exmaple
fmt.Println(strings.Contains("seafood", "foo"))
fmt.Println(strings.Contains("seafood", "bar"))
fmt.Println(strings.Contains("seafood", ""))
fmt.Println(strings.Contains("", ""))
- ouput
true
false
true
true
- Index查找位置
func Index(s, sep string) int
- 子串sep在字符串s中第一次出现的位置,不存在则返回-1。
- exmaple
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
- output
4
-1
ToLower, ToUpper
- ToLower字母小写
func ToLower(s string) string
- 返回将所有字母都转为对应的小写版本的拷贝。
- exmaple
fmt.Println(strings.ToLower("Gopher"))
- output
gopher
- ToUpper字母大写
func ToUpper(s string) string
-
返回将所有字母都转为对应的大写版本的拷贝
-
exmaple
fmt.Println(strings.ToUpper("Gopher"))
- output
GOPHER
Trim,TrimRight,TrimLeft
- Trim
func Trim(s string, cutset string) string
-
返回将s前后端所有cutset包含的utf-8码值都去掉的字符串。
-
exmaple
fmt.Printf("[%q]", strings.Trim(" !!! Achtung! Achtung! !!! ", "! "))
- output
["Achtung! Achtung"]
- TrimLeft
func TrimLeft(s string, cutset string) string
- 返回将s前端所有cutset包含的utf-8码值都去掉的字符串。
- TrimRight
func TrimRight(s string, cutset string) string
- 返回将s后端所有cutset包含的utf-8码值都去掉的字符串。