Go语言基础篇--5.Go的复合数据类型【数组array+切片slice+map】

1.数组(array)

概念:存储一组相同数据类型的容器

/*array 定义*/
var variable_name [SIZE] variable_type
/*定义*/
var array0 [5]int
var array1 = [5]int{54, 5}
var array2 = [5]int{2: 54, 4: 5}//将数组的第2位设置为54,第4位设置为5
var array3 = [...]int{54, 7: 5}//可不设置array的长度,会根据后续定义的数组自动确定数据长度

fmt.Println("数组长度:", len(array0))
fmt.Println("数组容量:", cap(array0))
fmt.Println(array2)		//output:[0,0,54,0,5]
fmt.Println(len(array3))//output:8

len(array)表示获取数组实际存储的数据长度
cap(array)表示数组本身可存储的数据容量

数组内存分析:在计算机内存空间中开辟连续内存空间,数组地址是数组中的首地址,也就是数组中第一个元素的地址。

数组访问
可使用range遍历数组

for i, v := range array3 {
	fmt.Printf("下标:%d 数值:%d\n", i, v)
}
//i表示读取到数组下标,v表示读取到的数组数据

数组类型:值类型
所谓的值类型指的是数组在传递过程中,传递的是数据副本。
例子:

var array00 = [5]int{54, 5}
array01 := array00
fmt.Println(array00)  //[54 5 0 0 0]
fmt.Println(array01)  //[54 5 0 0 0]
array01[0] = 100
fmt.Println(array00)  [54 5 0 0 0]
fmt.Println(array01)  //[100 5 0 0 0]

将array00赋值给array01,然后修改array01中第0位的数值,此时array00不变,array01的第0位数值发生改变,这就是值传递。在赋值过程中,实际上array00将自身的数据复制了一份,然后将复制的这份副本数据传递给array01,所以当array01数组改变自身数据时,并不影响array00的数据。

数组排序:

/*冒泡排序*/
var array_sort = [5]int{19, 23, 8, 10, 6}
for i := 1; i < len(array_sort); i++ {
	for j := 0; j < len(array_sort)-i; j++ {
		if array_sort[j] > array_sort[j+1] {
			array_sort[j], array_sort[j+1] = array_sort[j+1], array_sort[j]
		}
	}
	fmt.Println(array_sort)

}
fmt.Println(array_sort)

2.切片(slice)

概念:由于数组是定长的,定义数组时定义了数组的长度后便无法改变数组大小,而slice则弥补了这一缺陷,可以把slice理解为一种动态数组或变长数组。切片实际上是对数组的引用,切片本身是没有数据的,切片的数据来源于其指向的底层数组中的数据。

/*定义*/
var slice0 []int
fmt.Println(slice0)          //[]
var slice1 = []int{45, 5, 6} 
fmt.Println(slice1)			 //[45 5 6]
fmt.Printf("%T\n", slice1)	 //[]int

其定义方式与数组定义方式类似,只是无需确定size的大小
一般的,go中可通过make函数来创建切片
make函数是专门用来创建引用类型的数据,比如slice、map、chan

/*
make函数:func make(t Type, size ...IntegerType) Type
	第一个参数表示要创建的数据类型;
	第二个参数表示当前数据类型的长度;
	第三个参数表示其容量的大小
*/
slice2 := make([]int, 3, 7)
fmt.Println(slice2) //[0 0 0]
/*
append函数:专门用来对slice操作,向slice末端添加数值
	slice = append(slice, elem1, elem2)
	slice = append(slice, anotherSlice...)
*/
slice2 = append(slice2, 2, 7, 8, 9, 6, 3)
fmt.Println(slice2) //[0 0 0 2 7 8 9 6 3]
slice2 = append(slice2, slice1...)//向slice2中添加另一个slice,必须加...
fmt.Println(slice2) //[0 0 0 2 7 8 9 6 3 45 5 6]

切片内存分析:上面提到过,切片是引用类型的数据,其本身并不存放数值,切片存放的实际上是指向底层数组的地址。
切片在扩容时是在之前容量的基础上,成倍增长;
扩容之后,切片便会指向一个新的底层数组的地址

slice3 := make([]int, 0, 2)
fmt.Println(slice3)           							//[]                      		           
fmt.Printf("len:%d,cap:%d\n", len(slice3), cap(slice3)) //len:0,cap:2
fmt.Printf("%p\n", slice3)                              //0xc0000bc200

slice3 = append(slice3, 1, 2, 3)
fmt.Println(slice3)          							//[1 2 3]               		             
fmt.Printf("len:%d,cap:%d\n", len(slice3), cap(slice3)) //len:3,cap:4
fmt.Printf("%p\n", slice3)                              //0xc0000be040

还可通过在现有数组的基础上进行操作得到切片:在现有的数组上创建切片,在修改数组/切片的内容时,对应的切片/数组的内容也会随之改变;同时需要注意基于现有数组创建的切片的长度和容量大小;在对切片添加数据时,倘若未超出切片当前容量,那么该切片所对应的数组地址与原始数组地址是相同的,一旦需要扩容,则该切片所指向的数组地址与原始数组地址不同。

	a := [5]int{1, 2, 3, 4, 5}
	fmt.Println("------------已有数组创建切片----------------")
	s1 := a[:4]
	s2 := a[2:5] //左闭右开
	s3 := a[1:3]
	fmt.Print("a, s1, s2, s3:")
	fmt.Println(a, s1, s2, s3)
	fmt.Printf("a--->%p\n", &a)
	fmt.Printf("s1--->%p\n", s1)//这里注意不能加&,因为切片s1是引用类型数据,s1表示的就是所指向的底层数组的地址,如果写作&s1,那么获取到的则是切片的地址
	/*数组a的长度和容量都为5,对于s1来说,相当于取了a所有的数据作为一个切片,s1当前的长度和容量都是5
	s2是从a[2]开始取数据,取到a[4],其长度为2,其容量是len(a)-2=3
	s3是从a[1]开始取数据,取到a[2],其长度为2,其容量是len(a)-1=4*/
	fmt.Printf("s1 -->len:%d, cap:%d\n", len(s1), cap(s1)) //	s1 -->len:5, cap:5
	fmt.Printf("s2 -->len:%d, cap:%d\n", len(s2), cap(s2)) //	s2 -->len:3, cap:3
	fmt.Printf("s3 -->len:%d, cap:%d\n", len(s3), cap(s3)) //	s3 -->len:2, cap:4
	fmt.Println("------------更改数组内容----------------")
	a[3] = 444
	fmt.Print("a, s1, s2, s3:")
	fmt.Println(a, s1, s2, s3)
	fmt.Println("------------更改切片内容----------------")
	s2[0] = 300 //s2[0]实际上是a[2]
	fmt.Print("a, s1, s2, s3:")
	fmt.Println(a, s1, s2, s3)
	fmt.Println("------------切片添加内容----------------")
	s1 = append(s1, 777)
	fmt.Print("a, s1, s2, s3:")
	fmt.Println(a, s1, s2, s3)
	fmt.Println("------------切片添加内容并扩容----------------")
	s1 = append(s1, 9, 9, 9, 9)
	fmt.Print("a, s1, s2, s3:")
	fmt.Println(a, s1, s2, s3)
	fmt.Printf("a--->%p\n", &a)
	fmt.Printf("s1--->%p\n", s1)
/*output
------------已有数组创建切片----------------
a, s1, s2, s3:[1 2 3 4 5] [1 2 3 4] [3 4 5] [2 3]
a--->0xc000018480
s1--->0xc000018480
s1 -->len:4, cap:5
s2 -->len:3, cap:3
s3 -->len:2, cap:4
------------更改数组内容----------------
a, s1, s2, s3:[1 2 3 444 5] [1 2 3 444] [3 444 5] [2 3]
------------更改切片内容----------------
a, s1, s2, s3:[1 2 300 444 5] [1 2 300 444] [300 444 5] [2 300]
------------切片添加内容----------------
a, s1, s2, s3:[1 2 300 444 777] [1 2 300 444 777] [300 444 777] [2 300]
------------切片添加内容并扩容----------------
a, s1, s2, s3:[1 2 300 444 777] [1 2 300 444 777 9 9 9 9] [300 444 777] [2 300]
a--->0xc000018480
s1--->0xc00001e0f0
*/

切片类型:引用类型
在传递时是传递的引用地址,即指向底层数组的地址

3.map

概念:map实际上就是一些键值对(key-value),键对应着值

/*定义map*/
var map_variable map[key_data_type]value_data_type
var map0 map[int]string//方式一
var map1 = make(map[int]string)//方式二
map2 := map[string]float32{"C++":5.50,"GO":5.00}//方式三

map是引用类型,如果不初始化map就会产生一个nil map,相当于是一个空map。
对于方式一来说:其创建的是nil map,并未初始化,如果对其进行赋值的话会报错
而对于方式二来说,虽然没有赋值,但是已经将其初始化了,因此可进行赋值。因此在对map进行操作时可添加如下判断:

if map0 == nil {
	map0 = make(map[int]string)
}
/*获取map值*/
	map0[0] = "helloworld"
	map0[1] = "hellochina"
	v1, ok1 := map0[1]
	v2, ok2 := map0[2]
	fmt.Println(map0)//map[0:helloworld 1:hellochina]
	fmt.Println(v1, ok1)//hellochina true
	fmt.Println(v2, ok2)// false	
	/*获取map值时,如果索引号key之前并未赋值,
	那么返回的value则是该value类型的默认值,
	比如int-->0,float-->0.0,string-->""等等,
	为了判断key值是否存在,在获取map值时:
	可以采用value, ok := map[number]的操作,
	其中value返回的是以number(key值)为索引的value值,
	ok为true则表示当前key值存在,可获取到对应的value值,
	如果为false则表示key值不存在,获取到的是默认值*/
/*修改map的value值*/
    map0[1]="CHINA"//直接利用key值进行操作即可
/*删除map的value值*/
	delete(map0,1)//直接对key值进行操作即可
/*遍历map*/
	for key, value := range map0 {
		fmt.Printf("key:%d---->value:%s\n", key, value)
	}    
	map1[1] = "样"
	map1[11] = "我"
	map1[13] = "AI"
	map1[2] = "人工"
	map1[6] = "自动化"
	map_slice := make([]int, 0)
	for key, value := range map1 {
		fmt.Printf("key:%d---->value:%s\n", key, value)
		map_slice = append(map_slice, key)
	}
	fmt.Println(map_slice)
	/*冒泡排序*/
	/*
		for i := 1; i < len(map_slice); i++ {
			for j := 0; j < len(map_slice)-i; j++ {
				if map_slice[j] > map_slice[j+1] {
					map_slice[j], map_slice[j+1] = map_slice[j+1], map_slice[j]
				}
			}
		}
	*/
	/*sort包中的Ints函数对Int数据类型排序,默认是升序*/
	sort.Ints(map_slice)
	fmt.Println(map_slice)
	for i := 0; i < len(map_slice); i++ {
		fmt.Printf("key:%d---->value:%s\n", map_slice[i], map1[map_slice[i]])
	}
	for i := len(map_slice) - 1; i >= 0; i-- {
		fmt.Printf("key:%d---->value:%s\n", map_slice[i], map1[map_slice[i]])
	}

map类型:引用型,传递的是引用地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值