Go语言第十二课 集合类型

数组

什么是数组

这是一个古老的数据结构概念了:

有序同类定长的集合就是数组。

如何定义?

最标准的:

var arry_a [3]int = [3]int{1,2,3}

其他的

var arry_a [3]int //这种情况下,arry_a的三个元素将被赋默认零值

var arry_a [3]int =[3]int{1,2}//这种情况下,arry_a[2]将被赋默认零值

arry_a := [...]int{1,2,3}

arry_a := [3]int{1,2,3}

* 注意[3]int与[4]int是不同类型,不能把[4]int类型的值赋给[3]int类型的变量

* 强调一点,golang里面只有值传递,没有引用传递。所谓值传递可以看成暴力的内存拷贝。这一点在数组传值上体现地特别明显!

package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	b := a
	b[0] = 100
	fmt.Println(a[0])
	fmt.Println(b[0])
}

输出

1

100

如何遍历?

通常是通过关键字range,也可以按下标遍历

package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	for value := range a {
		fmt.Println(value)
	}
	fmt.Println("----------------")
	for i := 0; i < len(a); i++ {
		fmt.Println(a[i])
	}
}

输出

0
1
2
----------------
1
2
3

关于==和!=

package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	a1 := [3]int{1, 2, 3}
	fmt.Println(a == a1)
	fmt.Println(a != a1)
	a2 := [3]int{1, 3, 2}
	fmt.Println(a == a2)
	fmt.Println(a != a2)
}

结果

true
false
false

true

这说明:当且仅当数组元素全部对应相等时,两个数组才相等,否则不等。

* 注意,由于[3]int与[4]int是不同类型,所以其值不可比较(会导致语法错误)!

动态数组——切片

package main

import "fmt"

func main() {
	var sp_int = make([]int, 0)
	fmt.Println(len(sp_int))

	sp_int = append(sp_int, 1, 2, 3)
	fmt.Println(len(sp_int))

	sp_int_copy1 := append(sp_int, 4)
	fmt.Println(len(sp_int))
	fmt.Println(len(sp_int_copy1))

	sp_int_copy1[0] = 100
	fmt.Println(sp_int)
	fmt.Println(sp_int_copy1)

	sp_int_copy2 := sp_int
	sp_int_copy2[1] = 200
	fmt.Println(sp_int_copy1)
	fmt.Println(sp_int_copy2)

	var sp_int_copy3 = make([]int, 3)
	copy(sp_int_copy3, sp_int)
	sp_int_copy3[2] = 300
	fmt.Println(sp_int)
	fmt.Println(sp_int_copy3)
}

0
3
3
4
[100 2 3]
[100 2 3 4]
[100 200 3 4]
[100 200 3]
[100 200 3]
[100 200 300]

可以看到数组之间的赋值是引用赋值,其实所有new,make创建的对象都是引用赋值,因为他们创建出来的实际上是指针(这个C/C++的new很相似)。

另外,切片可定义最大长度:

test:= make([]int,3,5) //切片test初始长度是3,最长可扩充到5,用cap(test)可以得到test切片的最大长度

有趣的切片拼接

切片拼接用内建append函数,append有两种用法:

用法1:将单个元素拼接到切片 append(切片,元素1,元素2,...,元素n),这种用法第一个参数是需要拼接的切片,后面可以写不定数目的同类型元素。这些元素将按书写顺序拼接到切片上。

用法2:将另一个切片拼接到切片上append(切片,另一个切片...),这种用法要注意其语法现象,另一个切切片后要加三个点,声明所要拼接的是切片而不是单个元素。注意,切片一个一个地拼接,也就是说这种用法只能传入两个参数:带拼接切片,另一个切片。

无论是哪种用法,只要拼接没有超过原切片的容量,append将返回一个地址相同的新切片。新旧切片唯一的区别仅在于长度不同(地址,容量均相同)

切片拼接还有一个有趣地地方就是容量问题:当原切片容量到达极限仍容纳不下拼接对象后,append函数会返回一个新的切片(地址),原切片被原样保留,举例如下:

package main

import "fmt"

func main() {
	a := make([]int, 0, 10)
	b := append(a, 1, 2, 3)
	fmt.Println(len(a), cap(a), "-->", a)
	fmt.Printf("a-->%p\n", a)
	fmt.Println(len(b), cap(b), "-->", b)
	fmt.Printf("b-->%p\n", b)

	b = append(b, []int{4, 5, 6}...)
	fmt.Println(len(b), cap(b), "-->", b)
	fmt.Printf("b-->%p\n", b)

	fmt.Println("--------------------------------------")
	c := append(b, []int{7, 8, 9, 10, 11}...)
	fmt.Println(len(b), cap(b), "-->", b)
	fmt.Printf("b-->%p\n", b)
	fmt.Println(len(c), cap(c), "-->", c)
	fmt.Printf("c-->%p\n", c)
}

0 10 --> []
a-->0xc0000a6000
3 10 --> [1 2 3]
b-->0xc0000a6000
6 10 --> [1 2 3 4 5 6]
b-->0xc0000a6000
--------------------------------------
6 10 --> [1 2 3 4 5 6]
b-->0xc0000a6000
11 20 --> [1 2 3 4 5 6 7 8 9 10 11]
c-->0xc0000b0000

切片移除

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Println(len(a), cap(a), "-->", a)
	del_index := 3
	b := append(a[:del_index], a[del_index+1:]...)
	fmt.Println(len(b), cap(b), "-->", b)
}

9 9 --> [1 2 3 4 5 6 7 8 9]
8 9 --> [1 2 3 5 6 7 8 9]

数组和切片的区别

最显著的区别是,数组是值类型,切片是指针类型(实际上所有通过make和new创建的变量都是指针类型)

package main

import (
	"fmt"
	"reflect"
)

func main() {
	test1 := make([]int, 3)
	test1[0] = 1
	test1[1] = 2
	test1[2] = 3

	test2 := []int{1, 2, 3}

	test3 := [...]int{1, 2, 3}

	test11 := test1
	test11[0] = 123

	test22 := test2
	test22[0] = 123

	test33 := test3
	test33[0] = 123

	fmt.Println(test1, reflect.TypeOf(test1))
	fmt.Println(test2, reflect.TypeOf(test2))
	fmt.Println(test3, reflect.TypeOf(test3))
}

[123 2 3] []int
[123 2 3] []int
[1 2 3] [3]int
注意:test2也是切片类型,只是写法不同而已

如果需要将数组转换成切片:

a := [3]int{1, 2, 3}
b := a[:]
fmt.Println(reflect.TypeOf(a).String())
fmt.Println(reflect.TypeOf(b).String())

[3]int
[]int

关于make和new关键字

说起make就不得不说另一个关键字:new

package main

import (
	"fmt"
	"reflect"
	"strconv"
)

func main() {
	a := new([3]int)
	a[0] = 100
	a[1] = 200
	a[2] = 300
	b := make([]int, 3)
	fmt.Println(reflect.TypeOf(a).String() + "-->" + strconv.Itoa(len(a)))
	fmt.Println(reflect.TypeOf(b).String() + "-->" + strconv.Itoa(len(b)))

	for i := 0; i < len(a); i++ {
		fmt.Print(strconv.Itoa(a[i]) + " ")
	}
	fmt.Print("\n")
	for i := 0; i < len(a); i++ {
		fmt.Print(strconv.Itoa(b[i]) + " ")
	}
	fmt.Println("\n---------------------------------")
	for index, val := range a {
		fmt.Print(strconv.Itoa(index) + "->" + strconv.Itoa(val) + " ")
	}
	fmt.Print("\n")
	for index, val := range b {
		fmt.Print(strconv.Itoa(index) + "->" + strconv.Itoa(val) + " ")
	}
	fmt.Print("\n")

	aa := a
	aa[0] = 1000
	fmt.Println(a)
}

输出

*[3]int-->3
[]int-->3
100 200 300 
0 0 0 
---------------------------------
0->100 1->200 2->300 
0->0 1->0 2->0 
&[1000 200 300]

make只能建立slice,map和channel三种类型;new没有限制。

有趣的数组截取(注意语法现象)

func main() {
	a := make([]int, 4)
	a[0] = 1
	a[1] = 2
	a[2] = 3
	a[3] = 4
	b := a[2:]
	fmt.Println(b)

	c := new([4]int)
	c[0] = 10
	c[1] = 20
	c[2] = 30
	c[3] = 40
	d := c[2:]
	fmt.Println(d)
}

[3 4]
[30 40]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值