Go内建容器——Golang学习笔记3

数组

  • 数组的空间大小写在类型前;
  • 除常规遍历数组方法之外,还可以通过range关键字进行遍历;
  • 可通过 _ 省略变量,不仅仅在range中,任何地方都可以通过 _ 省略变量;
  • 数组是值类型,调用 func f(arr [10] int)会拷贝数组;
  • 数组是作为函数参数时是值类型,即拷贝;其他语言中获取数组名即获取数组的首元素地址,是引用传递;
  • [10] int 和[20] int是不同的数据类型;
  • Go中一般不直接使用数组。
  1. 数组定义
  • 数组定义的案例
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]]
  1. 数组遍历
  • 数组遍历的案例
// 遍历方法一 使用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])
}
  1. 数组参数传递

数组参数传递的案例

// 值传递
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

  1. 意义明确,美观
  2. c++:没有类似能力
  3. Java/Python:只能for each value,不能同时获取i, v

切片(Slice)

  1. 切片的定义
  • 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]
  1. 切片的引用传递
  • 切片的引用传递的案例
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]
  1. 切片自切
  • 切片自切的案例
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]
  1. 切片扩展
  • 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
  1. 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
  1. 向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来接受这三个值;

  1. 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
  1. 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;
  1. 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[]
  1. 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
  1. 获得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
  1. 删除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
  1. Fields 空格分割
func Fields (s string) []string
  • 返回将字符串按照空格(连续的空格被认为一个空格进行分割)分割的多个字符串。如果字符串全部是空格或者是空字符串的话,会返回空切片。
  • example
fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
  • output
Fields are: ["foo" "bar" "baz"]
  1. 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" " "]
[""]
  1. 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
  1. 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
  1. 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
  1. ToLower字母小写
func ToLower(s string) string
  • 返回将所有字母都转为对应的小写版本的拷贝。
  • exmaple
fmt.Println(strings.ToLower("Gopher"))
  • output
gopher
  1. ToUpper字母大写
func ToUpper(s string) string
  • 返回将所有字母都转为对应的大写版本的拷贝

  • exmaple

fmt.Println(strings.ToUpper("Gopher"))
  • output
GOPHER
Trim,TrimRight,TrimLeft
  1. Trim
func Trim(s string, cutset string) string
  • 返回将s前后端所有cutset包含的utf-8码值都去掉的字符串。

  • exmaple

fmt.Printf("[%q]", strings.Trim(" !!! Achtung! Achtung! !!! ", "! "))
  • output
["Achtung! Achtung"]
  1. TrimLeft
func TrimLeft(s string, cutset string) string
  • 返回将s前端所有cutset包含的utf-8码值都去掉的字符串。
  1. TrimRight
func TrimRight(s string, cutset string) string
  • 返回将s后端所有cutset包含的utf-8码值都去掉的字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值