Golang中的复合数据类型
1.数组
数组是指一系列同一类型数据的集合
数组定义
var 数组变量名 [元素数量]T
数组的初始化
数组有四种初始化方法
数组的遍历
可以用for循环和for-range循环对数组进行遍历
数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值
多维数组
Go语言支持多维数组(数组中又嵌套数组)
var variable_name [size1][size2]....[sizeN] variable_type
二维数组为例:
var 数组变量名 [元素数量][元素数量]T
2.切片
切片(Slice)是一个拥有相同各类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容
切片是一个引用类型,它的内部结构包含地址、长度和容量。
声明切片类型的基本语法如下:
var name []T
其中:
- name:表示变量名
- T:表示切片中的元素类型
关于nil的认识
当你声明了一个变量,还没有赋值时,golang中会自动给你的变量赋一个默认零值。以下是每种类型对应的零值
bool -> false
numbers -> 0
string -> ""
pointers -> nil
slices -> nil
maps -> nil
functions -> nil
interfaces -> nil
切片的循环遍历
切片的循环遍历和数组的循环遍历是一样的
基于数组定义切片
由于切片的底层就是一个数组,所以我们可以基于数组定义切片
切片再切片
基于切片定义切片
关于切片的长度和容量
切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量
切片的长度就是它所包含的元素个数
切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数
切片s的长度和容量可通过表达式len(s)和cap(s)来获取
切片的本质
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
使用make函数构造切片
如果需要动态的创建一个切片,需要使用内置的make()函数,格式如下
make([]T,size,cap)
其中:
- T:切片的元素类型
- size:切片中元素的数量
- cap:切片的容量
使用append()函数对切片扩容
Go语言的内建函数append()可以为切片动态添加元素,每个切片会指向一个底层数组,
使用copy()函数复制切片
从切片中删除元素
Go语言中没有删除切片元素的专用方法,可以使用切片本身的特性来删除元素
Golang切片排序算法以及sort包
选择排序
通过比较 首先选出最小的数放在第一个位置上,然后再其余的数中选出次小数放在第二个位置上,以此类推,直到所有的数成为有序序列
冒泡排序
从头到尾,比较相邻两个匀速的大小,如果符合交换条件,交换两个元素的位置。
Golang内置Sort包对切片进行排序
对int、float64和string数组或是切片的排序,go分别体统了sort.Ints(), sort.Float65s()和sort.Strings()函数,默认都是从小到大排序
3.map
map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用
语法:
map[keyType]ValueType
其中:
- keyType:表示键的类型
- ValueType:表示键对应的值的类型
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。
make:用于slice,map和channel的初始化
make(map[keyType]valueType,[cap])
cap表示map的容量,该参数是可选的
map的循环遍历
Go语言中使用for range遍历map
map类型数据的CURD
判断某个键是否存在
value,exist_flag := map对象[key]
使用delete()函数删除键值对
使用delete()内建函数从map中删除一组键值对,delete函数格式如下:
delete(map对象,key)
其中:
map对象:表示要删除键值对的map对象
key:表示要函数的键值对的键
元素为map类型的切片
代码:
package main
import (
"fmt"
// "sort"
"strings"
)
func main() {
//1. 数组
// //数组的长度时类型的一部分
// var arr1 [3]int
// var arr2 [4]int
// var strArr [3]string
// fmt.Printf("arr1的类型:%T,arr2的类型:%T,strArr的类型:%T",arr1,arr2,strArr)//arr1的类型:[3]int,arr2的类型:[4]int,strArr的类型:[3]string
//数组的初始化 第一种方法
// var arr1 [3]int
// fmt.Println(arr1)//[0 0 0]
// arr1[0] = 23
// arr1[1] = 10
// arr1[2] = 24
// fmt.Println(arr1)//[23 10 24]
// var arr2 [3]string
// fmt.Println(arr2)//[ ]
// arr2[0] = "php"
// arr2[1] = "java"
// arr2[2] = "golang"
// // arr2[3] = ".net"//invalid array index 3 (out of bounds for 3-element array)
// fmt.Println(arr2)//[php java golang]
//数组的初始化 第二种方法
// var arr1 = [3]int{23,24,25}
// fmt.Println(arr1)//[23 24 25]
// var arr2 = [3]string{"golang","java","php"}
// fmt.Println(arr2)//[golang java php]
// arr3 := [3]string{"golang","java","php"}
// fmt.Println(arr3)//[golang java php]
////数组的初始化 第三种方法 一般情况下我们可以让编译器根据初始值的个数自行推断数据的长度
// var arr1 = [...]int{1,2,3,4,5,6,7,8,9}
// fmt.Println(arr1)//[1 2 3 4 5 6 7 8 9]
// fmt.Println(len(arr1))//9,len()打印数组的长度
//注意数组的长度
// var arr2 = [...]string{"golang","javaScript","java","php","python"}
// // arr2[5] = "c#"//invalid array index 5 (out of bounds for 5-element array)
// fmt.Println(arr2)
// fmt.Println(len(arr2))
//改变数组里面的值
// var arr3 = [...]string{"golang","javaScript","java","php","python"}
// arr3[1] = "c#"
// fmt.Println(arr3)//[golang c# java php python]
//数组的初始化 第四种方法
// arr := [...]int{0:1,1:10,2:20,5:50}//index:value
// fmt.Println(len(arr))//6
// fmt.Println(arr)//[1 10 20 0 0 50]
//数组的循环遍历
// var arr1 = [3]int{23, 24, 25}
// for i := 0; i < len(arr1); i++ {
// fmt.Println(arr1[i])
// }
// var arr2 = [...]string{"golang", "javaScript", "java", "php", "python"}
// for k, v := range arr2 {
// fmt.Printf("key:%v,value:%v
", k, v)
// }
//请求出一个数组里面元素的和以及这些数的平均值。分别用for和for-range实现
// var arr = [...]int{23,56,243,78,99}
// var sum = 0
// var sum1 = 0
// for i := 0; i < len(arr); i++ {
// sum += arr[i]
// }
// fmt.Printf("arr数组元素的和是:%v 平均值:%.2f",sum,float64(sum)/float64(len(arr)))
// for _,v := range arr {
// sum1 += v
// }
// fmt.Println(sum1)
//请求出一个数组的最大值,并得到对应的下标
// var intArr = [...]int{1,-1,12,65,11}
// var max = intArr[0]
// var index = 0
// for k,v := range intArr {
// if v > max {
// max = v
// index = k
// }
// }
// fmt.Printf("intArr数组的最大值是:%v,对应的下标是:%v",max,index)
//从数组[1,3,5,7,8]中找出和为8的两个元素的下标
// var arr = [...]int{1,3,5,7,8}
// for i := 0; i < len(arr); i++ {
// for j := i+1; j < len(arr); j++{
// if arr[i]+arr[j] == 8 {
// fmt.Printf("数组中和为8的两个元素的下标分别为:(%v,%v)
",i,j)
// }
// }
// }
/*
值类型:改变变量副本值的时候,不会改变变量本身的值
引用类型:改变副本值的时候,会改变变量本身的值
*/
//值类型 引用类型
//值类型:Golang中,基本数据类型和数组都是值类型
// var a = 10
// b := a
// fmt.Println(a,b)//10 10
// a = 20
// fmt.Println(a,b)//20 10
// var arr1 = [...]int{1,2,3}
// arr2 := arr1
// arr1[0] = 11
// fmt.Println(arr1)//[11 2 3]
// fmt.Println(arr2)//[1 2 3]
//引用类型:切片
// var arr1 = []int{1,2,3}
// arr2 := arr1
// arr1[0] = 11
// fmt.Println(arr1)//[11 2 3]
// fmt.Println(arr2)//[11 2 3]
// var arr1 = []int{1,2,3,4}
// arr2 := arr1
// arr2[0] = 111
// fmt.Println(arr1)//[11 2 3]
// fmt.Println(arr2)//[11 2 3]
//多维数据
//二位数组的定义
// var arr = [3]int{1,2,3}//一维数组
// var arr = [3][2]string{
// {"北京","上海"},
// {"广州","深圳"},
// {"成都","重庆"},
// }
// fmt.Println(arr[0][0])//北京
// for _, v1 := range arr {
// for _, v2 := range v1 {
// fmt.Println(v2)
// }
// }
//定义二位数组的另一种方式
// var arr = [...][2]string{
// {"北京","上海"},
// {"广州","深圳"},
// {"成都","重庆"},
// }
// fmt.Println(arr)//[[北京 上海] [广州 深圳] [成都 重庆]]
// var arr = [3][...]string{//多维数组只有第一层可以使用...来让编译器推导数组长度,否则:use of [...] array outside of array literal
// {"北京","上海"},
// {"广州","深圳"},
// {"成都","重庆"},
// }
// fmt.Println(arr)
//2. 切片
//切片的声明和初始化
// var arr1 []int
// fmt.Printf("%v - %T - 长度;%v
", arr1, arr1, len(arr1)) //[] - []int - 长度;0
// var arr2 = []int{1, 2, 3, 4, 56}
// fmt.Printf("%v - %T - 长度;%v
", arr2, arr2, len(arr2)) //[1 2 3 4 56] - []int - 长度;5
// var arr3 = []int{1:2, 2:4,3:6, 5:6}
// fmt.Printf("%v - %T - 长度;%v
", arr3, arr3, len(arr3)) //[0 2 4 6 0 6] - []int - 长度;6
// var arr1 []int
// var arr2 = []int {1,2,3,4}
// fmt.Println(arr1)//[]
// fmt.Println(arr1 == nil)//true,golang中声明切片后 切片的默认值是nil
// fmt.Println(arr2 == nil)//false
//切片的循环遍历
// var strSlice = []string{"php","java","js","golang"}
// for i := 0; i<len(strSlice);i++{
// fmt.Println(strSlice[i])
// }
// for k,v := range strSlice {
// fmt.Println(k,v)
// }
//基于数组定义切片
// a := [5]int{55,56,57,58,59}
// b := a[:]//获取数组里面的所有值
// fmt.Printf("%v--%T
",b,b)//[55 56 57 58 59]--[]int
// c := a[1:4]
// fmt.Printf("%v--%T
",c,c)//[56 57 58]--[]int
// d := a[2:]
// fmt.Printf("%v--%T
",d,d)//[57 58 59]--[]int
// e := a[:3]//表示获取第三个下标前面的数据
// fmt.Printf("%v--%T
",e,e)//[55 56 57]--[]int
//切片再切片
// a := []string{"北京","上海","广州","深圳","成都","重庆"}
// b := a[1:]
// fmt.Printf("%v--%T
",b,b)//[上海 广州 深圳 成都 重庆]--[]string
//关于切片的长度和容量
//长度:切片的长度就是它所包含的元素个数
//容量:切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数
// s := []int{2,3,4,5,76,11,23}
// fmt.Printf("长度%d,容量%d
",len(s),cap(s))//长度7,容量7
// a := s[2:]
// fmt.Printf("长度%d,容量%d
",len(a),cap(a))//长度5,容量5
// b := s[1:3]
// fmt.Printf("长度%d,容量%d
",len(b),cap(b))//长度2,容量6
// c := s[:3]
// fmt.Printf("长度%d,容量%d
",len(c),cap(c))//长度3,容量7
//make函数创建切片
// var sliceA = make([]int,4,8)
// // fmt.Println(sliceA)//[0 0 0 0]
// fmt.Printf("长度%v,容量:%v", len(sliceA),cap(sliceA))//长度4,容量:8
// sliceA[0] = 10
// sliceA[1] = 20
// sliceA[2] = 30
// sliceA[3] = 40
// fmt.Println(sliceA)//[10 20 30 40]
// sliceB := []string{"php","java","go"}
// sliceB[2] = "golang"
// fmt.Println(sliceB)//[php java golang]
// var sliceC []int
// fmt.Printf("切片sliceC:%v,长度%v,容量:%v
",sliceC, len(sliceC),cap(sliceC))//切片sliceC:[],长度0,容量:0
//golang中不能通过下标的方式给切片扩容
// sliceC[0] = 1//runtime error: index out of range [0] with length 0
// fmt.Println(sliceC)
//Go语言的内建函数append()可以为切片动态添加元素,每个切片会指向一个底层数组,
// sliceC = append(sliceC,12)
// fmt.Printf("切片sliceC:%v,长度%v,容量:%v
",sliceC, len(sliceC),cap(sliceC))//切片sliceC:[12],长度1,容量:1
// sliceC = append(sliceC,24)
// fmt.Printf("切片sliceC:%v,长度%v,容量:%v
",sliceC, len(sliceC),cap(sliceC))//切片sliceC:[12 24],长度2,容量:2
// var sliceD []int
// sliceD = append(sliceD,12,23,4,5455)
// fmt.Printf("切片sliceD:%v,长度%v,容量:%v
",sliceD, len(sliceD),cap(sliceD))//切片sliceD:[12 23 4 5455],长度4,容量:4
//append方法可以合并切片
// sliceA := []string{"java","php"}
// sliceB := []string{"golang","js"}
// sliceA = append(sliceA,sliceB...)
// fmt.Printf("切片sliceD:%v,长度%v,容量:%v
",sliceA, len(sliceA),cap(sliceA))//切片sliceD:[java php golang js],长度4,容量:4
//切片的扩容策略
// var sliceA []int
// for i :=1; i< 10; i++ {
// sliceA = append(sliceA,i)
// fmt.Printf("%v,长度%v,容量:%v
",sliceA, len(sliceA),cap(sliceA))
// }
/*
输出结果为:
[1],长度1,容量:1
[1 2],长度2,容量:2
[1 2 3],长度3,容量:4
[1 2 3 4],长度4,容量:4
[1 2 3 4 5],长度5,容量:8
[1 2 3 4 5 6],长度6,容量:8
[1 2 3 4 5 6 7],长度7,容量:8
[1 2 3 4 5 6 7 8],长度8,容量:8
[1 2 3 4 5 6 7 8 9],长度9,容量:16
*/
//切片就是引用类型
// sliceA := []int{1,2,3,4,5}
// sliceB := sliceA//把sliceB指向sliceA的存储空间
// sliceB[0] = 11
// fmt.Println(sliceA)//[11 2 3 4 5]
// fmt.Println(sliceB)//[11 2 3 4 5]
//copy()函数复制切片
// sliceA := []int{1,2,3,4,5}
// sliceB := make([]int,5,5)
// copy(sliceB,sliceA)
// fmt.Println(sliceA)//[1 2 3 4 5]
// fmt.Println(sliceB)//[1 2 3 4 5]
// sliceB[0] = 11
// fmt.Println(sliceA)//[1 2 3 4 5]
// fmt.Println(sliceB)//[11 2 3 4 5]
//Go语言中没有删除切片元素的专用方法,可以使用切片本身的特性来删除元素
// a := []int{30,31,32,33,34,35,36,37}
// //删除索引为2的元素 注意:append合并切片的时候最后一个元素要加...
// a = append(a[:2],a[3:]...)
// fmt.Println(a)//[30 31 33 34 35 36 37]
//利用切片更改字符串
// s1 := "big"
// byteStr := []byte(s1)
// byteStr[0] = 'p'
// fmt.Println(string(byteStr))//pig
// s2 := "你好golang"
// runeStr := []rune(s2)
// fmt.Println(runeStr)//[20320 22909 103 111 108 97 110 103]
// runeStr[0] = '大'
// fmt.Println(string(runeStr))//大好golang
//Golang切片排序算法以及sort包
//从数组[1,3,5,7,8]中找出和为8的两个元素的下标
// var arr = [...]int{1,3,5,7,8}
// for i := 0; i < len(arr); i++ {
// for j := i+1; j < len(arr); j++{
// if arr[i]+arr[j] == 8 {
// fmt.Printf("数组中和为8的两个元素的下标分别为:(%v,%v)
",i,j)
// }
// }
// }
/*
选择排序:进行从小到大排序
概念:通过比较 首先选出最小的数放在第一个位置上,然后再其余的数中选出次小数放在第二个位置上,以此类推,直到所有的数成为有序序列
*/
// var numSlice = []int{9,8,7,6,5,4}
// for i := 0; i < len(numSlice); i++ {
// for j :=i+1; j<len(numSlice); j++ {
// if numSlice[i] > numSlice[j] {
// tmp := numSlice[i]
// numSlice[i] = numSlice[j]
// numSlice[j] = tmp
// }
// }
// }
// fmt.Println(numSlice)//[9 8 7 6 5 4]
/*
冒泡排序
概念:从头到尾,比较相邻两个匀速的大小,如果符合交换条件,交换两个元素的位置。
特点:每一轮比较中,都会选出一个最大的数,放在正确的位置
*/
//冒泡排序从小到大
// var nums = []int{9,8,7,6,5,4}
// for i := 0; i < len(nums); i++ {
// for j :=0; j<len(nums)-1-i; j++ {
// if nums[j] > nums[j+1] {
// temp := nums[j]
// nums[j] = nums[j+1]
// nums[j+1] = temp
// }
// }
// }
// fmt.Println(nums)//[4 5 6 7 8 9]
//内置Sort包对切片进行排序
//sort升序排序
// intList := []int{2,4,3,5,7,6,9,8,1,0}
// floatList := []float64{4.2,5.9,12.4,10.2,50.7,99.9,31.4,27.81828,3.14}
// stringList := []string{"a","c","b","z","x","w","y","d","f","i"}
// sort.Ints(intList)
// sort.Float64s(floatList)
// sort.Strings(stringList)
// fmt.Println(intList)//[0 1 2 3 4 5 6 7 8 9]
// fmt.Println(floatList)//[3.14 4.2 5.9 10.2 12.4 27.81828 31.4 50.7 99.9]
// fmt.Println(stringList)//[a b c d f i w x y z]
//sort降序排序
// sort.Sort(sort.Reverse(sort.IntSlice(intList)))
// sort.Sort(sort.Reverse(sort.Float64Slice(floatList)))
// sort.Sort(sort.Reverse(sort.StringSlice(stringList)))
// fmt.Println(intList)//[9 8 7 6 5 4 3 2 1 0]
// fmt.Println(floatList)//[99.9 50.7 31.4 27.81828 12.4 10.2 5.9 4.2 3.14]
// fmt.Println(stringList)//[z y x w i f d c b a]
//map
//1.make创建map类型的数据
// var userinfo = make(map[string]string)
// userinfo["username"] = "zhangsan"
// userinfo["age"] = "20"
// userinfo["sex"] = "Male"
// fmt.Println(userinfo)//map[age:20 sex:Male username:zhangsan]
//2.map也支持再声明的时候填充元素
// userinfo := map[string]string{
// "username": "zhangsan",
// "age": "20",
// "sex": "Male",
// }
// fmt.Println(userinfo) //map[age:20 sex:Male username:zhangsan]
//map类型的循环遍历
// userinfo := map[string]string{
// "username":"zhangsan",
// "age":"20",
// "sex":"Male",
// }
// // fmt.Println(userinfo["username"])//zhangsan
// for k,v := range userinfo{
// fmt.Printf("key:%v,value:%v
",k,v)
// }
/*
key:username,value:zhangsan
key:age,value:20
key:sex,value:Male
*/
//map类型的CRUD
//1.创建 修改map类型的数据
// var userinfo = make(map[string]string)
// userinfo["username"] = "zhangsan"
// userinfo["age"] = "20"
// userinfo["sex"] = "Male"
// fmt.Println(userinfo)//map[age:20 sex:Male username:zhangsan]
//2.创建 修改map类型的数据
// userinfo := map[string]string{
// "username":"zhangsan",
// "age":"20",
// "sex":"Male",
// }
// userinfo["username"] = "lisi"
// fmt.Println(userinfo)//map[age:20 sex:Male username:lisi]
//3.获取 查找map类型的数据
// var userinfo = map[string]string{
// "username": "zhangsan",
// "age": "20",
// "sex": "Male",
// }
// // v, exist := userinfo["age"]
// // fmt.Println(v, exist)//20 true
// v, exist := userinfo["hobby"]
// fmt.Println(v, exist)// false,返回空和false,v表示key对应的值,exist表示key是否存在再当前map中
//4.删除map数据里面的key以及对应的值
// var userinfo = map[string]string{
// "username": "zhangsan",
// "age": "20",
// "sex": "Male",
// "height": "180cm",
// }
// fmt.Println(userinfo)//map[age:20 height:180cm sex:Male username:zhangsan]
// delete(userinfo, "sex")
// fmt.Println(userinfo)//map[age:20 height:180cm username:zhangsan]
//元素为map类型的切片
//在切片里面放一系列用户信息,可以定义一个元素为map类型的切片
// var userinfo = []string{"zhangsan","lisi"}
// var userinfo = make([]map[string]string, 2, 2)
// // fmt.Println(userinfo[0])//map[] 没有初始化的map的默认值nil
// if userinfo[0] == nil {
// userinfo[0] = make(map[string]string)
// userinfo[0]["username"] = "zhansan"
// userinfo[0]["age"] = "20"
// userinfo[0]["sex"] = "Male"
// }
// if userinfo[1] == nil {
// userinfo[1] = make(map[string]string)
// userinfo[1]["username"] = "lisi"
// userinfo[1]["age"] = "22"
// userinfo[1]["sex"] = "Male"
// }
// fmt.Println(userinfo)//[map[age:20 sex:Male username:zhansan] map[age:22 sex:Male username:lisi]]
//循环遍历元素为map类型切片
// for _, v := range userinfo {
// for key, value := range v {
// fmt.Printf("key=%v,value:%v ",key,value)
// }
// fmt.Println()
// }
/*
输出:
key=username,value:zhansan key=age,value:20 key=sex,value:Male
key=age,value:22 key=sex,value:Male key=username,value:lisi
*/
//map类型数据的值可以是切片
// var userinfo = make(map[string][]string)
// userinfo["hobby"] = []string{
// "eat",
// "sleep",
// "coding",
// }
// userinfo["work"] = []string{
// "js",
// "java",
// "golang",
// }
// // fmt.Println(userinfo) //map[hobby:[eat sleep coding] work:[js java golang]]
// //循环遍历
// for _, v := range userinfo {
// for k1, v1 := range v {
// fmt.Printf("key=%v, value=%v ", k1, v1)
// }
// fmt.Println()
// }
// /*
// key=0, value=eat key=1, value=sleep key=2, value=coding
// key=0, value=js key=1, value=java key=2, value=golang
// */
//map类型是引用数据类型
// var userinfo1 = make(map[string]string)
// userinfo1["username"] = "zhangsan"
// userinfo1["age"] = "20"
// userinfo2 := userinfo1
// userinfo2["username"] = "lisi"
// fmt.Println(userinfo1)//map[age:20 username:lisi]
// fmt.Println(userinfo2)//map[age:20 username:lisi]
// //改变副本的值,原始数据也被改变,所以map类型是引用数据类型
//map的排序
// map1 := make(map[int]int, 10)
// map1[10] = 100
// map1[8] = 13
// map1[5] = 56
// map1[2] = 90
// // fmt.Println(map1)
// for k, v := range map1 {
// fmt.Println(k, v)
// }
// /*
// 输出:
// 2 90
// 10 100
// 8 13
// 5 56
// */
//按照key升序输出map的key=>value
//1.把map的key放在切片里面
// var keySlice []int
// for key, _ := range map1 {
// keySlice = append(keySlice, key)
// }
// fmt.Println(keySlice) //[8 10 5 2]
// //2.让key进行升序排序
// sort.Ints(keySlice)
// fmt.Println(keySlice) //[2 5 8 10]
// //3.循环遍历key,输出map的值
// for _, v := range keySlice {
// fmt.Printf("key=%v value=%v
", v, map1[v])
// }
// /*
// 输出:
// key=2 value=90
// key=5 value=56
// key=8 value=13
// key=10 value=100
// */
//写一个程序,统计一个字符串中每个单词出现的次数。比如"how do you do"中how=1, do=2, you=1.
//字符串,切片,map的综合使用
var str = "how do you do"
var strSlice = strings.Split(str, " ")
fmt.Println(strSlice)//[how do you do]
var strMap = make(map[string]int)
for _, v :=range strSlice {
strMap[v]++
}
fmt.Println(strMap)//map[do:2 how:1 you:1]
}