欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

go语言系列-从数组到map

程序员文章站 2022-05-04 11:15:32
数组 数组可以存放多个同一类型数据。数组也是一种数据类型,在Go中,数组是值类型 一个养鸡场有6只鸡,它们的体重分别是3kg,5kg,1kg,3.4kg,2kg,50kg。请问这六只鸡的总体重是多少?平均体重是多少? 数组的使用 go func main(){ //1)创建一个byte类型的26个元 ......

数组

数组可以存放多个同一类型数据。数组也是一种数据类型,在go中,数组是值类型

一个养鸡场有6只鸡,它们的体重分别是3kg,5kg,1kg,3.4kg,2kg,50kg。请问这六只鸡的总体重是多少?平均体重是多少?

使用传统的方法不利于数据的管理和维护
传统的方法不够灵活,因此我们引出需要学习的新的数据类型  ==》数组

//使用数组的方式来解决问题
var 数组名 [数组大小]数据类型
var a [5]int
赋初值 a[0] = 1 a[1] = 30 ...
func main()  {
   //1.定义一个数组
   var hens [7]float64
   //2. 给数组的每个元素赋值,元素的下标是从0开始的 0 - 6
   hens[0] = 3.0  //hens数组的第1个元素 hens[0]
   hens[1] = 5.0  //hens数组的第2个元素 hens[1]
   hens[2] = 1.0
   hens[3] = 3.4
   hens[4] = 2.0
   hens[5] = 50.0
   hens[6] = 150.0 //增加一只鸡
   //3. 遍历数组求出总体重
   totalweight := 0.0
   for i := 0; i < len(hens); i++ {
      totalweight += hens[i]
   }
   //4. 求出平均体重
   avgweight := fmt.sprintf("%.2f",totalweight / float64(len(hens)))
   fmt.printf("totalweight = %v avgweight = %v",totalweight,avgweight)
}
//输出:totalweight = 214.4 avgweight = 30.63
使用数组来解决问题,增加程序的可维护性
而且方法代码更加清晰,也容易扩展

数组在内存布局
go语言系列-从数组到map

数组的地址可以通过数组名来获取 &intarr
数组的第一个元素的地址,就是数组的首地址
数组的各个元素的地址间隔是依据数组的类型决定,比如 int64 -> 8    int32 -> 4 ...
func main()  {
   var intarr [3]int  //int占8个字节
   //当我们定义完数组后,其是数组的各个元素有默认值 0
   fmt.println(intarr) // 默认值
   intarr[0] = 10
   intarr[1] = 20
   intarr[2] = 30
   fmt.println(intarr)
   fmt.printf("intarr的地址 = %p intarr[0] 地址 = %p intarr[1] 地址 = %p intarr[2] 地址 = %p",
      &intarr,&intarr[0],&intarr[1],&intarr[2])
}
//输出:[0 0 0]
//[10 20 30]
//intarr的地址 = 0xc000010380 intarr[0] 地址 = 0xc000010380 intarr[1] 地址 = 0xc000010388 intarr[2] 地址 = 0xc000010390

数组的使用

func main()  {
   var score [5]float64
   for i := 0; i < len(score); i++ {
      fmt.printf("请输入第%d个元素的值\n",i+1)
      fmt.scanln(&score[i])
   }
   //变量数组打印
   for i := 0; i < len(score); i++ {
      fmt.printf("score[%d] = %v\t",i,score[i])
     //访问数组元素:数组名[下标]比如:要使用a数组的第三个元素 a[2]
   }
}

初始化数组的方式

func main() {
   //四种初始化数组的方式
   var numarr01 [3]int = [3]int{1,2,3}
   fmt.println("numarr01 = ",numarr01)

   var numarr02  = [3]int{4,5,6}
   fmt.println("numarr02 = ",numarr02)

   //这里的 [...]是规定的写法
   var numarr03 = [...]int{7,8,9}
   fmt.println("numarr03 = ",numarr03)

   var numarr04 = [...]int{1: 800, 0: 900, 2: 999}
   fmt.println("numarr04 = ",numarr04)

   //类型推导
   strarr05 := [...]string{1: "zisefeizhu", 0: "jack", 2: "mary"}
   fmt.println("strarr05 = ",strarr05)
}
//输出:numarr01 =  [1 2 3]
//numarr02 =  [4 5 6]
//numarr03 =  [7 8 9]
//numarr04 =  [900 800 999]
//strarr05 =  [jack zisefeizhu mary]

数组的遍历

方法1:  常规遍历
遍历数组求出总体重
   totalweight := 0.0
   for i := 0; i < len(hens); i++ {
      totalweight += hens[i]
   }

方法2:for - range 结构遍历
第一个返回值index是数组的下标
第二个value是在该下标位置的值
它们都是仅在for循环内部可见的局部变量
遍历数组元素的时候,如果不想使用下标index,可以直接把下边index标为下划线_
index和value的名称不是固定的,即程序员可以自行指定,一般命名为index和value
 //演示for-range遍历数组
   heroes := [...]string{"宋江","吴用","林冲"}
   for i, v := range heroes {
      fmt.printf("i = %v v = %v\n",i, v)
      fmt.printf("heroes[%d] = %v \n",i,heroes[i])
   }
   for _,v := range heroes {
      fmt.printf("元素的值 = %v\n",v)
   }
}
//输出:i = 0 v = 宋江
//heroes[0] = 宋江 
//i = 1 v = 吴用
//heroes[1] = 吴用 
//i = 2 v = 林冲
//heroes[2] = 林冲 
//元素的值 = 宋江
//元素的值 = 吴用
//元素的值 = 林冲

数组使用的注意事项和细节

数组是多个相同类型数组的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化
var arr []int 这时 arr就是一个slice切片,切片后面专门讲解
数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混合
数组创建后,如果没有赋值,有默认值(零值)
	数值类型数组:默认值为0
	字符串数组:  默认值为" "
	bool数组:   默认值为false
使用数组的步骤 
	1.声明数组并开辟空间
	2.给数组各个元素赋值(默认零值)
	3.使用数组
数组的下标是从0开始的
数组下标必须在指定范围内使用,否则报panic:数组越界,比如
	var arr[5]int 则有效下标为 0 - 4
go的数组属于值类型,在默认情况下是值类型,因此会进行值拷贝。数组间不会相互影响

go语言系列-从数组到map

如果想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)

go语言系列-从数组到map

长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度

go语言系列-从数组到map

数组的应用案例

创建一个byte类型的26个元素的数组,分别放置’a’ - ’z’。使用for循环访问所有元素并打印出来。提示:字符数据运算’a’+1 ->’b’

func main(){
   //1)创建一个byte类型的26个元素的数组,分别放置’a’ - ’z’。
   // 使用for循环访问所有元素并打印出来。
   // 提示:字符数据运算’a’+1 ->’b’

   //思路
   //1. 声明一个数组 var mychars [26]byte
   //2. 使用for循环,利用 字符可以进行运算的特点来辅助 'a' + 1 = 'b'
   //3. 使用for 循环打印
   var mychars [26]byte
   for i := 0; i < 26; i++ {
      mychars[i] = 'a' + byte(i) //注意需要将i =》 byte
   }
   for i := 0; i < 26; i++ {
      fmt.printf("%c",mychars[i])
   }
//输出:abcdefghijklmnopqrstuvwxyz
}

请求出一个数组的最大值,并得到对应的下标

func main() {
   //思路
   //1. 声明一个数组 var intarr[5] = [...]int{1,-1,9,90,11}
   //2. 假定第一个元素就是最大值,下标就是0
   //3. 然后从第二个元素开始循环比较,如果发现有更大值,则交换
   var intarr [6]int = [...]int {1,-1,9,90,11,9000}
   maxval := intarr[0]
   maxvalindex := 0
   for i := 1; i < len(intarr); i++ {
      //从第二个元素开始循环比较,如果发现有更大,则交换
      if maxval < intarr[i] {
         maxval = intarr[i]
         maxvalindex = i
      }
   }
   fmt.printf("maxval = %v maxvalindex = %v", maxval, maxvalindex)
}
//输出:maxval = 9000 maxvalindex = 5

请求出一个数组的和和平均值。for-range

func main() {
   //思路
   //1. 声明一个数组 var intarr[5] = [...]int{1,-1,9,90,11}
   //2. 求出和sum
   //3. 求出平均值
   var intarr [5]int = [...]int{1, -1, 9, 90, 12}
   sum := 0
   for _,val := range intarr {
      //累计求和
      sum += val
   }
   //如何让平均值保留到小数
   fmt.printf("sum = %v 平均值 = %v", sum, float64(sum) / float64(len(intarr)) )
}
//输出:sum = 111 平均值 = 22.2

要求:随机生成五个数,并将其反转打印,复杂应用

import (
   "fmt"
   "math/rand"
   "time"
)

func main() {
   //思路
   //1. 随机生成5个数,rand.intn()函数
   //2. 当我们得到随机数后,就放到一个数组int数组
   //3. 反转打印,交换的次数是len/2,倒数第一个和第一个元素交换,倒数第二个和第二个元素交换
   var intarr [5]int
   //为了每次生成的随机数都不一样,我们需要给一个seed值
   len := len(intarr)
   rand.seed(time.now().unixnano())
   for i := 0; i < len; i++ {
      intarr[i] = rand.intn(100)  //0 <= n < 100
   }
   fmt.println("交换前",intarr)
   //反转打印,交换的次数是 len / 2
   //倒数第一个和第一个元素交换,倒数第二个和第二个元素交换
   temp := 0 //做一个临时变量
   for i := 0; i < len / 2; i++ {
      temp = intarr[len - 1 - i]
      intarr[len - 1 -i] = intarr[i]
      intarr[i] = temp
   }
   fmt.println("交换后",intarr)
}
//输出:交换前 [24 15 90 17 6]
//交换后 [6 17 90 15 24]

数组练习题

题目要求:

跳水比赛 8个评委打分,运动员的成绩去掉一个最高分,去掉一个最低分,剩下的6个分数的平均分就是最后得分,使现

(1)请把最高分,最低分的评委找出

(2)找出最佳评委和最差评委。最佳评委是最后得分差距最小,最差评委最后得分差距最大

分析:

设计一个函数求最高分 最低分 平均分 需要考虑存在多个最低分和最高分的情况

找最有裁判和最差裁判使用abs() 以及切片完成 将绝对值传入到切片中再遍历

package main

import (
   "fmt"
   "math")
func max(array  *[8]float64) (mx float64,mi float64,avg float64){
   max := 0.0
   for i := 0; i < len(array); i++ {
      if (*array)[i] > max {
         max = (*array)[i]
      }
   }
   min := max
   for i := 0; i < len(array); i++ {
      if (*array)[i] < min  {
         min = (*array)[i]
      }
   }
   mx = max
   mi = min
   sum := 0.0
   maxcount,mincount :=0.0,0.0
   for i := 0; i < len(array); i++ {
       //判断最大值和最小值出现的次数 1次时直接去掉  多次是需要加上去
      if (*array)[i] == max {
         maxcount +=1
      }
      if (*array)[i] == min{
         mincount +=1
      }
      //算出不包含任意一次不包含 最大值、最小值的和
      if (*array)[i] != max && (*array)[i] != min{
         sum += (*array)[i]
      }

   }
   //fmt.println(maxcount,mincount)
   //处理出现多次最大值或者最小值
   if  mincount > 1.0 && maxcount > 1.0{
      sum += (max*(maxcount-1)+min*(mincount-1))
   }else if mincount > 1.0 && maxcount == 1.0{
      sum += (min*(mincount-1))
   }else if mincount ==1.0  && maxcount > 1.0{
      sum += (max*(maxcount-1))
   }else {
       sum += 0
   }
   avg = sum/6.0
   return  mx,min,avg}

func best(array1  *[8]float64, arry2 []float64, avg float64) {

   for i := 0; i < len(array1); i++ {
      arry2 = append(arry2, math.abs((*array1)[i]-avg))
   }
   max  := 0.0
   for j :=0;j < len(arry2);j++{
      if arry2[j] > max{
         max = arry2[j]
      }
   }
   min := max
   for i := 0; i < len(arry2); i++ {
      if arry2[i] < min  {
         min = arry2[i]
      }
   }
   for i := 0; i < len(arry2); i++ {
      if arry2[i] == min {
         fmt.printf("最优秀评分者为第%v位裁判,评分:%v 和平均分相差%v\n",i+1,(*array1)[i],min)
      }
   }

   for i := 0; i < len(arry2); i++ {
      if arry2[i] == max  {
         fmt.printf("评分差距最大者为第%v位裁判,评分:%v和平均分相差%v\n",i+1,(*array1)[i],max)
      }
   }}

var socre [8]float64
var socreabs = make([]float64,0,0)
func main() {
   //输入8个裁判的分数
   for  i := 0;i<len(socre);i++{
      fmt.printf("请输入第%v位裁判的的评分:\n",i+1)
      fmt.scanln(&socre[i])
   }

   max,min,avg :=max(&socre)
   fmt.println(socre)
   fmt.printf("最高分为:%v,最低分为:%v 平均分为:%v\n",max,min,avg)
   //知道最大分  最小分 找最大分、最小分的裁判
   for k :=0;k<len(socre);k++{
      if socre[k] == max{
         fmt.printf("最高分为:%v,第%v位裁判\n",max,k+1)
      }else if socre[k] == min {
         fmt.printf("最低分:%v,第%v位裁判\n",min,k+1)
      }
   }
   best(&socre,socreabs,avg)

}

切片

切片的英文是slice
切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度len(slice)都一样
切片的长度是可以变化的,因此切片是一个可以动态变化的数组

var 切片名 []类型
比如: var  a  []int

func main()  {
   //演示切片的基本使用
   var intarr [5]int = [...]int {1,22,33,66,99}
   //声明/定义一个切片
   //1. slice := intarr[1:3]
   //2. intarr[1:3]表示slice引用到intarr这个数组
   //3. 引用intarr数组的起始下标为1,最后的下标为3(但是不包含3)
   slice := intarr[1:3]
   fmt.println("intarr = ",intarr)
   fmt.println("slice 的元素是 = ",slice) // 22,33
   fmt.println("slice 的元素个数 = ",len(slice)) // 2
   fmt.println("slice 的容量 = ", cap(slice)) //切片的容量是可以动态变化
}
//输出:intarr =  [1 22 33 66 99]
//slice 的元素是 =  [22 33]
//slice 的元素个数 =  2
//slice 的容量 =  4

切片在内存中形式【重要】

go语言系列-从数组到map

对上面的分析图总结

slice的确是一个引用类型
slice从底层来说,其实就是一个数据结构(struct结构体)
	type slice struct {
		ptr *[2]int
		len int
		cap int
	}

切片的使用

方式1:定义一个数组,然后让切片去引用一个已经创建好的数组
func main()  {
   //演示切片的基本使用
   var intarr [5]int = [...]int {1,22,33,66,99}
   //声明/定义一个切片
   //1. slice := intarr[1:3]
   //2. intarr[1:3]表示slice引用到intarr这个数组
   //3. 引用intarr数组的起始下标为1,最后的下标为3(但是不包含3)
   slice := intarr[1:3]
   fmt.println("intarr = ",intarr)
   fmt.println("slice 的元素是 = ",slice) // 22,33
   fmt.println("slice 的元素个数 = ",len(slice)) // 2
   fmt.println("slice 的容量 = ", cap(slice)) //切片的容量是可以动态变化
}
//输出:intarr =  [1 22 33 66 99]
//slice 的元素是 =  [22 33]
//slice 的元素个数 =  2
//slice 的容量 =  4

方式2:通过make来创建切片
var 切片名 []type = make([]type,len,[cap])
参数说明:
	type:就是数据类型
	len :大小
	cap :指定切片容量,可选,如果分配了cap,则要求cap >= len 
func main() {
   //演示切片的使用 make
   var slice []float64 = make([]float64, 5, 10)
   slice[1] = 10
   slice[3] = 20
   //对于切片,必须make使用
   fmt.println(slice)
   fmt.println("slice的size = ",len(slice))
   fmt.println("slice的cap  = ",cap(slice))
}
//输出:[0 10 0 20 0]
//slice的size =  5
//slice的cap  =  10

go语言系列-从数组到map

对上面代码的小结:
1)通过make方式创建切片可以指定切片的大小和容量
2)如果没有给切片的各个元素赋值,那么就会使用默认值[int, float => 0 string => “ ” bool => false]
3)通过make方式创建的切片对应的数组是由make底层维护,对外不可见,即只能通过slice去访问各个元素

方式3:定义一个切片,直接就指定具体数组,使用原理类似make的方式
func main() {
   var strslice []string = []string {"zisefeizhu","zhujingxing","mayike"}
   fmt.println("strslice = ", strslice)
   fmt.println("strslice size = ",len(strslice))
   fmt.println("strslice cap = ",cap(strslice))
}
//输出:strslice =  [zisefeizhu jingxing mayike]
//strslice size =  3
//strslice cap =  3

方式1和方式2的区别(面试)
	方式1是直接引用数组,这个数组是事先存在的,程序员是可见的
	方式2是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的,make创建切片的示意图

go语言系列-从数组到map

切片的遍历

func main()  {
   //使用常规的for循环遍历切片
   var arr [5]int = [...]int {10, 20, 30, 40, 50}
   slice := arr[1:4] // 20, 30, 40
   for i := 0; i < len(slice); i++ {
      fmt.printf("slice[%v] = %v \t",i , slice[i])
   }
   fmt.println()

   //使用for - range 方式遍历切片
   for i, v := range slice {
      fmt.printf("i = %v v = %v \n",i ,v)
   }
}

切片使用的注意事项和细节

slice扩充缩容会有内存申请释放也是开销,且扩容好像是1.25倍
切片初始化时 var slice = arr[startindex:endindex]
	说明:从arr数组下标为startindex,取到下标为endindex的元素(不含arr[endindex])
切片初始化时,仍然不能越界。范围在[0 - len(arr)]之间,但是可以动态增长
	var slice = arr[0:end] 可以简写 var slice = arr[:end]
	var slice = arr[start:len(arr)] 可以简写:var slice = arr[start:]
	var slice = arr[0:len(arr)] 可以简写:var slice = arr[:]
cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用
切片可以继续切片
	func main()  {
   	var arr [5]int = [...]int {10, 20, 30, 40, 50}
   	slice := arr[1:4] // 20, 30, 40
   	slice2 := slice[1:2] // slice [20,30,40] [30]
   	slice2[0] = 100 //因为arr slice 和slice2 指向的数据空间是一个,因此slice2[0] =100
   	fmt.println("slice2 = ",slice2)
   	fmt.println("slice = ",slice)
   	fmt.println("arr = ",arr)
	}
append内置函数,可以对切片进行动态追加
	func main()  {
  	 //用append内置函数,可以对切片进行动态追加
   	var slice []int = []int {100,200,300}
   	//通过append直接给slice追加具体的元素
   	slice = append(slice,400,500,600)
  	fmt.println("slice",slice) //100, 200, 300, 400, 500, 600
   	//通过append将切片slice 追加给slice
   	slice = append(slice,slice...)   
  	fmt.println("slice",slice) ////slice [100 200 300 400 500 600 100 200 300 400 500 600]
  }
	切片append操作的底层原理分析
		切片append操作的本质就是对数组扩容
		go底层会创建一下新的数组newarr(安装扩容后大小)
		将slice原来包含的元素拷贝到新的数组newarr
		slice 重新引用到newarr
		注意newarr是在底层来维护的,程序员不可见

go语言系列-从数组到map

切片的拷贝操作
	切片使用copy内置函数完成拷贝,举例说明
	func main()  {
   	//切片的拷贝操作
   	//切片使用copy内置函数完成拷贝,举例说明
   	var slice []int = []int {1,2,3,4,5}
   	var slice2 = make([]int,10)
   	copy(slice2,slice)
   	fmt.println("slice = ",slice)  //slice =  [1 2 3 4 5]
   	fmt.println("slice2 = ",slice2) //slice2 =  [1 2 3 4 5 0 0 0 0 0]
	}
	copy(para1,para2)参数的数据类型是切片
	按照上面的代码来看,slice和slice2的数据空间是独立,相互不影响,也就是说slice[0] = 999,slice5[0] 仍然是1
关于拷贝的注意事项
	func main()  {
   	var a []int = []int {1,2,3,4,5}
   	var slice = make([]int,1)
   	fmt.println(slice)
   	copy(slice,a)
   	fmt.println(slice)
	}
	//输出:[0]
	//[1]	
切片是引用类型,所以在传递时,遵守引用传递机制。看两段代码,并分析底层原理

go语言系列-从数组到map

string和slice

string底层是一个byte数组,因此string也可以进行切片处理

func main(){
   str := "hello@zisefeizhu"
   //使用切片获取到zisefeizhu
   slice := str[6:]
   fmt.println("slice = ",slice)
}
//输出:slice =  zisefeizhu

string和切片在内存的形式,以”abcd”画出内存示意图
go语言系列-从数组到map
string是不可变的,也就是说不能通过str[0] = ‘z’ 方式来修改字符串
go语言系列-从数组到map
如果需要修改字符串,可以先将string -> []byte /或者 []rune -> 修改 -> 重写转成string

如果需要修改字符串,可以先将string -> []byte  /或者  []rune -> 修改 -> 重写转成string
   //"hello@zisefeizhu" => 改成 "zello@zisefeizhu"
   arr1 := []byte(str)
   arr1[0] = 'z'
   str = string(arr1)
   fmt.println("str = ",str)
   //细节:我们转成[]byte后,可以处理英文和数字,但是不能处理中文
   //原因是[]byte字节来处理,而一个汉字,是3个字节,因此就会出现乱码
   //解决方法是 将 string 转成 []rune 即可,因为[]rune 是按字符处理,兼容汉字
   arr2 := []rune(str)
   arr2[0] = '北'
   str = string(arr1)
   fmt.println("str = ", str)
}
//输出:slice =  zisefeizhu
//str =  zello@zisefeizhu
//str =  zello@zisefeizhu

切片练习题

说明:编写一个函数 fbn(n int),要求完成

  1. 可以接收一个n int

  2. 能够将斐波那契的数列放到切片中

  3. 提示,斐波那契的数列形式:

arr[0] = 1; arr[1] = 1; arr[2] = 2; arr[3] = 3; arr[4] = 5; arr[5] = 8

package main

import "fmt"

func fbn(n int) ([]uint64) {
   //声明一个切片,切片大小n
   fbnslice := make([]uint64, n)
   //第一个数和第二个数的斐波那契为 1
   fbnslice[0] = 1
   fbnslice[1] = 1
   //进行for循环来存放斐波那契的数列
   for i := 2; i < n; i++ {
      fbnslice[i] = fbnslice[i - 1] + fbnslice[i - 2]
   }
   return fbnslice
}
func main()  {
   /*
   1)可以接收一个n int
   2)能够将斐波那契的数列放到切片中
   3)提示,斐波那契的数列形式:
   arr[0] = 1; arr[1] = 1; arr[2] = 2; arr[3] = 3; arr[4] = 5; arr[5] = 8
   思路
   1. 声明一个函数fbn(n int) ([]uint64)
   2. 编程fbn(n int) 进行for循环来存放斐波那契的数列 0 =》1  1 =》 1
    */
   fnbslice := fbn(20)
   fmt.println("fnbslice = ",fnbslice)
}
//输出:fnbslice =  [1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765]

排序

排序

排序是将一组数据,依指定的顺序进行排序的过程

排序的分类
1)内部排序:
	指将需要处理的所有数据都加载到内部存储器中进行排序
	包括(交换式排序法、选择式排序法和插入式排序法);
2)外部排序法
	数据量过大,无法全部加载到内存中,需要借助外部存储进行排序
	包括(合并排序法和直接合并排序法)

冒泡排序的思路分析

go语言系列-从数组到map
go语言系列-从数组到map

冒泡排序实现

//冒泡排序
func bubblesort(arr *[5]int)  {
   fmt.println("排序前arr = ",(*arr))
   temp := 0 //临时变量(用于做交换)

   //冒泡排序:一步一步推导出来的
   for i := 0; i < len(*arr) - 1; i++ {
      for j := 0; j < len(*arr) -1 - i; j++ {
         if (*arr)[j] < (*arr)[j + 1] {
            //交换
            temp = (*arr)[j]
            (*arr)[j] = (*arr)[j + 1]
            (*arr)[j + 1] = temp
         }
      }
   }
   fmt.println("排序后arr  = ",(*arr))
}
func main()  {
   //定义数组
   arr := [...]int {24,69,80,57,13}
   //将数组传递给一个函数,完成排序
   bubblesort(&arr)
   fmt.println("main arr = ",arr) //有序? 是有序的
}
//输出:排序前arr =  [24 69 80 57 13]
//排序后arr  =  [80 69 57 24 13]
//main arr =  [80 69 57 24 13]

优化
//冒泡排序
func bubblesort(arr []int) []int {
   fmt.println("排序前arr = ", arr)
   flag := true
   //冒泡排序:一步一步推导出来的
   for i := 0; i < len(arr) - 1; i++ {
      for j := 0; j < len(arr) -1 - i; j++ {
         if arr[j] < arr[j + 1] {
            //交换
            swap(arr, j , j+1)
            flag = false
         }
      }
      //优化不必要的交换
      if flag {
         break
      }
   }
   return arr
}

func swap(arr []int, i int, j int )  {
   temp := arr[i]
   arr[i] = arr[j]
   arr[j] = temp
}

func main()  {
   //定义数组
   arr := []int {24,69,80,57,13}
   //将数组传递给一个函数,完成排序
   num := bubblesort(arr)
   fmt.println("main num = ",num)
}
//排序前arr =  [24 69 80 57 13]
//main num =  [80 69 57 24 13]

冒泡排序练习题

package main
//要求:随机生成5个元素的数组,并使用冒泡排序对其排序  从小到大
//思路分析:
//随机数用math/rand生成为了更好的保证其不会重复 使用 rand.new(rand.newsource(time.now().unixnano()))并定义一个随机生成函数
//用冒泡排序对其排序
import (
   "fmt"
   "math/rand"
   "time"
)

var  arrnum [5]int = [5]int{109,137,49,190,87}
//定义冒泡函数
//重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从a到z)
// 错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成
func bubblesort( arrary *[5]int){
   //第一次比较
   fmt.println("排序前arr=",(*arrary))
   tmp :=0
   for  j := 0 ; j <len(arrary)-1 ;j++{
      for  i :=0;i <len(arrary)-1-j ;i++ {
         if arrary[i] > arrary[i+1]{
            tmp = arrary[i]
            arrary[i] = arrary[i+1]
            arrary[i+1] = tmp
         }
      }
   }

   fmt.println("排序后arr=",(*arrary))

}


var  arrname  [5]int

func main() {
   r := rand.new(rand.newsource(time.now().unixnano()))//生成随机数字
   for  i := 0; i < len(arrname) ; i++ {
      arrname[i] = r.intn(20000) //指定生成随机数的范围
   }
   bubblesort(&arrname)

}

查找

在go中,常用的查找有两种:

  1. 顺序查找

  2. 二分查找(该数组是有序)

案例演示

1)有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王

猜数游戏:从键盘中任意输入一个名称,判断数列中是否包含此名称【顺序查找】

func main()  {
   names := [4]string{"白眉鹰王","金毛狮王","紫衫龙王","青翼蝠王"}
   var heroname = ""
   fmt.println("请输入要查找的人名...")
   fmt.scanln(&heroname)
   //顺序查找:第一种方式
   for i := 0; i < len(names); i++ {
      if heroname == names[i] {
         fmt.printf("找到%v 下标%v \n",heroname,i)
         break
      } else if i == (len(names) -1 ) {
         fmt.printf("没有找到%v \n",heroname)
      }
   }

   //顺序查找:第二种方式【推荐...】
   index := -1
   for i := 0; i < len(names); i++ {
      if heroname == names[i] {
         index = i //将找到的值对应的下标赋给index
         break
      }
   }
   if index != -1 {
      fmt.printf("找到%v,下标%v \n",heroname,index)
   } else {
      fmt.println("没有找到",heroname)
   }
}
//输出:请输入要查找的人名...  白眉鹰王   找到白眉鹰王 下标0   找到白眉鹰王,下标0 
  1. 请对一个有序数组进行二分查找 {1,8,10,89,1000,1234},输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示”没有这个数”【会使用到递归】

二分查找的思路分析
go语言系列-从数组到map

//二分法查找
/*
   二分查找思路:比如要查找的数是findval
   1. arr是一个有序数组,并且是从小到大排序
   2. 先找到中间的下标middle = (leftindex + rightindex)/2,然后让中间下标的值和findval进行比较
   2.1  如果arr[middle] > findval  就应该向 leftindex --- (middle - 1)
   2.2  如果arr[middle] < findval  就应该向 (middle + 1) --- rightindex
   2.3  如果arr[middle] == findval  就找到
   2.4  上面的2.1 2.2 2.3 的逻辑会递归执行
   3. 想一下,怎么样的情况下,就说明找不到【分析出退出递归的条件!!】
   if leftindex > rightindex {
      //找不到...
      return ...
   }
 */

func binaryfind(arr *[6]int, leftindex int, rightindex int, findval int)  {
   //判断leftindex是否大于rightindex
   if leftindex > rightindex {
      fmt.println("找不到")
      return
   }
   //先找到 中间的下标
   middle := (leftindex + rightindex) / 2
   if (*arr)[middle] > findval {
      //说明要查找的数,应该在leftindex -- (middle - 1)
      binaryfind(arr, leftindex, middle - 1, findval)
   } else if (*arr)[middle] < findval {
      //说明要查找的数,应该在middle + 1 -- rightindex
      binaryfind(arr, middle + 1, rightindex, findval)
   } else {
      //找到了
      fmt.printf("找到了,下标为%v \n",middle)
   }
}
func main()  {
   arr := [6]int {1,8,89,1000,1234}
   //测试
   binaryfind(&arr,0,len(arr) - 1, 1234)
}
//输出:找到了,下标为4 

二维数组

请用二维数组输出如下图形
go语言系列-从数组到map

func main()  {
   //定义/声明二维数组
   var arr [4][6]int
   //赋初值
   arr[1][2] = 1
   arr[2][1] = 2
   arr[2][3] = 3
   //遍历二维数组,按照要求输出图形
   for i := 0; i < 4; i++ {
      for j := 0; j < 6; j++ {
         fmt.print(arr[i][j]," ")
      }
      fmt.println()
   }
}
//0 0 0 0 0 0 
//0 0 1 0 0 0 
//0 2 0 3 0 0 
//0 0 0 0 0 0 

二维数组在内存的存在形式

go语言系列-从数组到map

二维数组在声明/定义时的写法

1)var 数组名 [大小][大小]类型 = [大小][大小]类型{{初值...},{初值...}}
2)var 数组名 [大小][大小]类型 = [...][大小]类型{{初值...},{初值...}}
3)var 数组名  = [大小][大小]类型{{初值...},{初值...}}
4)var 数组名 = [...][大小]类型{{初值...},{初值...}}

二维数组的遍历

func main()  {
   //演示二维数组的遍历
   var arr = [2][3]int{{1,2,3},{4,5,6}}
   //for循环遍历
   for i := 0; i < len(arr); i++ {
      for j := 0; j < len(arr[i]); j++ {
         fmt.printf("%v\t",arr[i][j])
      }
      fmt.println()
   }
   //for-range遍历二维数组
   for i, v := range arr {
      for j, v2 := range v {
         fmt.printf("arr[%v][%v]=%v\t",i,j,v2)
      }
      fmt.println()
   }
}

二维数组的应用案例

定义二维数组,用于保存三个班,每个班五名同学成绩,

并求出每个班级平均分、以及所有班级平均分

func main()  {
   //定义二维数组,用于保存三个班,每个班五名同学成绩,
   //并求出每个班级平均分、以及所有班级平均分
   //1.定义二维数组
   var scores [3][5]float64
   //2.循环的输入成绩
   for i := 0; i < len(scores); i++ {
      for j := 0; j < len(scores[i]); j++ {
         fmt.printf("请输入第%d班的第%d个学生的成绩\n",i+1, j+1)
         fmt.scanln(&scores[i][j])
      }
   }
   //fmt.println(scores)
   //3.遍历输出成绩后的二维数组,统计平局分
   totalsum := 0.0 //定义一个变量,用于累计所有班级的总分
   for i := 0; i < len(scores); i++ {
      sum := 0.0 //定义一个变量,用于累计各个班级的总分
      for j := 0; j < len(scores[i]); j++ {
         sum += scores[i][j]
      }
      totalsum += sum
      fmt.printf("第%d班级的总分为%v,平均分%v\n", i + 1, sum, sum / float64(len(scores[i])))
   }
   fmt.printf("所有班级的总分为%v,所有班级平均分%v\n", totalsum, totalsum / 15 )
}

二维数组练习题

转置概念:矩阵的行列互换得到的新矩阵称为转置矩阵,而二维数组就是我们通常说的矩阵。

需求:使用go语言方法实现二维数组(3*3)的矩阵的转置

转置前:

​ [ 0, 1, 2]

​ [ 4, 5, 6]

​ [ 8, 9, 10]

转置后

​ [ 0, 4, 8]

​ [ 1, 5, 9]

​ [ 2, 6, 10]

type   num struct {

}

func (array  num ) upserver(aaaay3 [3][3]int)  {
   for i :=0; i<len(aaaay3);i++{
      for  j:=0;j<i;j++{
         aaaay3[i][j],aaaay3[j][i] = aaaay3[j][i],aaaay3[i][j]
      }
   }
   fmt.println(aaaay3)
}

func (array  num ) upserver2(aaaay3 [3][3]int)  {
   temparry :=[3][3]int{}
   for i :=0; i<len(aaaay3);i++{
      for  j:=0;j<i;j++{
         temparry[i][j]=aaaay3[i][j]
         aaaay3[i][j] =aaaay3[j][i]
         aaaay3[j][i]=temparry[i][j]
      }
   }
   fmt.println(aaaay3)
}

func main() {
   arrinfo :=num{

   }
   aeey :=[3][3]int{
      {0, 1, 2} ,   /*  第一行索引为 0 */
      {4, 5, 6} ,   /*  第二行索引为 1 */
      {8, 9, 10}}
   fmt.println(aeey)
   fmt.println("****")
   arrinfo.upserver(aeey)
   arrinfo.upserver2(aeey)
}

map

map是key-value数据结构,又称为字段或者关联数组。类似其它编程语言的集合,在编程中是经常使用到的

v 内部实现

map是给予散列表来实现,就是我们常说的hash表,所以我们每次迭代map的时候,打印的key和value是无序的,每次迭代的都不一样,即使我们按照一定的顺序存在也不行。

map的散列表包含一组桶,每次存储和查找键值对的时候,都要先选择一个桶。如何选择桶呢?就是把指定的键传给散列函数,就可以索引到相应的桶了,进而找到对应的键值。

这种方式的好处在于,存储的数据越多,索引分布越均匀,所以我们访问键值对的速度也就越快,当然存储的细节还有很多,大家可以参考hash相关的知识,这里我们记住map存储的是无序的键值对集合

map的声明

var 变量名 map[keytype]valuetype
key可以是什么类型
	go中的map的key可以是很多种类型,比如bool、数字、string、指针、channel,还可以是只包含前面几个类型的 接口、结构体、数组
	通常key为int、string
	注意:slice、map还有function不可以,因为这几个没法用 == 来判断
valuetype可以是什么类型
	valuetype的类型和key基本一样
	通常为:数字(整数,浮点数)、string、map、struct

map声明的举例:
	var a map[string]string
	var a map[string]int
	var a map[int]string
	var a map[string]map[string]string
	注意:声明是不会分配内存的,初始化需要make,分配内存后才能赋值和使用

func main()  {
   //map的声明和注意事项
   var a map[string]string
   //在使用map前,需要先make,make的作用就是给map分配数据空间
   a = make(map[string]string,10)
   a["no1"] = "松江"
   a["no2"] = "无用"
   a["no1"] = "武松"
   a["no3"] = "无用"
   fmt.println(a)
}
//输出:map[no1:武松 no2:无用 no3:无用]
对上面代码的说明
	1)map在使用前一定要make
	2)map的key是不能重复的,如果重复了,则以最后这个key-value为准
	3)map的value是可以相同的
	4)map的key-value是无序的
	5)make内置函数数目

map的使用

方式1:
func main()  {
   //第一种使用方式
   var a map[string]string
   //在使用map前,需要先make,make的作用就是给map分配数据空间
   a = make(map[string]string,10)
   a["no1"] = "松江"
   a["no2"] = "无用"
   a["no1"] = "武松"
   a["no3"] = "无用"
   fmt.println(a)
}

方式2:
func main()  {
   //第二种方式
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   fmt.println(cities)
//输出:map[no1:北京 no2:天津 no3:上海]
  
方式3:
func main()  {
	//第三种方式
	heroes := map[string]string {
		"her01" : "宋江",
		"her02" : "吴用",
		"her03" : "林冲",
	}
	heroes["her04"] = "武松"
	fmt.println("heroes = ",heroes)
}
//heroes =  map[her01:宋江 her02:吴用 her03:林冲 her04:武松] 

map的增删改查操作

map增加和更新
map["key"] = value //如果key还没有,就是增加,如果key存在就是修改

func main(){
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   fmt.println(cities)
   //因此no3 这个key已经存在,因此下面的这句就是修改
   cities["no3"] = "上海~"
   fmt.println(cities)
}
//输出:map[no1:北京 no2:天津 no3:上海]
//map[no1:北京 no2:天津 no3:上海~]

map删除

delete(map,"key"),delete是一个内置函数,如果key存在,就删除该key-value,如果key不存在,不操作,但是也不会报错
go语言系列-从数组到map

func main(){
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   fmt.println(cities)  //map[no1:北京 no2:天津 no3:上海]
   //因此no3 这个key已经存在,因此下面的这句就是修改
   cities["no3"] = "上海~"
   fmt.println(cities)  //map[no1:北京 no2:天津 no3:上海~]
   //演示删除
   delete(cities,"no1")
   fmt.println(cities)  //map[no2:天津 no3:上海~]
   //当delete指定的key不存在时,删除不会操作,也不会报错
   delete(cities,"no4")
   fmt.println(cities)  //map[no2:天津 no3:上海~]
  
如果要删除map的所有key,没有一个专门的方法一次删除,可以遍历一下key,逐个删除或者map = make(...),make一个新的,让原来的成为垃圾,被gc回收
func main(){
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   fmt.println(cities)  //map[no1:北京 no2:天津 no3:上海]
   //因此no3 这个key已经存在,因此下面的这句就是修改
   cities["no3"] = "上海~"
   fmt.println(cities)  //map[no1:北京 no2:天津 no3:上海~]
   //演示删除
   delete(cities,"no1")
   fmt.println(cities)  //map[no2:天津 no3:上海~]
   //当delete指定的key不存在时,删除不会操作,也不会报错
   delete(cities,"no4")
   fmt.println(cities)  //map[no2:天津 no3:上海~]
   //如果希望一次性删除所有的key
   //1. 遍历所有的key,遍历逐一删除
   //2. 直接make一个新的空间
   cities = make(map[string]string)
   fmt.println(cities)  //map[]
}  

map查找

func main(){
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   fmt.println(cities)  //map[no1:北京 no2:天津 no3:上海]
   //因此no3 这个key已经存在,因此下面的这句就是修改
   cities["no3"] = "上海~"
   fmt.println(cities)  //map[no1:北京 no2:天津 no3:上海~]
   //演示map 的查找
   va1, ok := cities["no2"]
   if ok {
      fmt.printf("有no2 key值为%v\n",va1)  //有no2 key值为天津
   } else {
      fmt.printf("没有no2 key\n")
   }
}

对上面代码的说明:
说明:如果cities这个map中存在”no2”,那么ok就会返回true,否则返回false

map遍历

案例演示相对复杂的map遍历:该map的value又是一个map

说明:map的遍历使用for-range的结构遍历

func main()  {
   //使用for-range遍历map
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   for k,v := range cities {
      fmt.printf("k = %v v = %v\n",k,v)
   }
   //使用for-range遍历一个结构比较复杂的map
   studentmap := make(map[string]map[string]string)
   studentmap["stu01"] = make(map[string]string, 3)
   studentmap["stu01"]["name"] = "tom"
   studentmap["stu01"]["sex"] = "男"
   studentmap["stu01"]["address"] = "北京长安街"

   studentmap["stu02"] = make(map[string]string, 3)  //这句话不能少!!!
   studentmap["stu02"]["name"] = "mary"
   studentmap["stu02"]["sex"] = "女"
   studentmap["stu02"]["address"] = "上海黄埔江"

   for k1, v1 := range studentmap {
      fmt.println("k1 = ",k1)
      for  k2, v2 := range v1 {
         fmt.printf("\t k2 = %v v2 = %v \n",k2,v2)
      }
      fmt.println()
   }
}
//输出:k = no1 v = 北京
//k = no2 v = 天津
//k = no3 v = 上海
//k1 =  stu01
//  k2 = name v2 = tom 
//  k2 = sex v2 = 男 
//  k2 = address v2 = 北京长安街 
//
//k1 =  stu02
//  k2 = name v2 = mary 
//  k2 = sex v2 = 女 
//  k2 = address v2 = 上海黄埔江 

map的长度

go语言系列-从数组到map

func main()  {
   //使用for-range遍历map
   cities := make(map[string]string)
   cities["no1"] = "北京"
   cities["no2"] = "天津"
   cities["no3"] = "上海"
   for k,v := range cities {
      fmt.printf("k = %v v = %v\n",k,v)
   }
   fmt.println(len(cities))  //3
}

map切片

切片的数据类型如果是map,则我们称为slice of map,map切片,这样使用则map个数就可以动态变化了

案例演示

要求:使用一个map来记录monster的信息name和age,也就是说一个monster对应一个map,并且妖怪的个数可以动态的增加=》map切片

package main

import (
   "fmt"
   _ "unicode"
)

func main()  {
   var monsters []map[string]string
   monsters = make([]map[string]string,2) //准备放入两个妖怪
   //2. 增加第一个妖怪的信息
   if monsters[0] == nil {
      monsters[0] = make(map[string]string,2)
      monsters[0]["name"] = "牛魔王"
      monsters[0]["age"] = "500"
   }

   if monsters[1] == nil {
      monsters[1] = make(map[string]string, 2)
      monsters[1]["name"] = "玉兔精"
      monsters[1]["age"] = "400"
   }
   //下面这个写法越界
   //if monsters[2] == nil {
   //    monsters[2] = make(map[string]string, 2)
   //    monsters[2]["name"] = "狐狸精"
   //    monsters[2]["age"] = "300"
   // }

   //这里需要使用到切片的append函数,可以动态的增加monster
   //1. 先定义monster信息
   newmonster := map[string]string {
      "name" : "新的妖怪-孙悟空",
      "age" : "1500",
   }
   monsters = append(monsters,newmonster)

   fmt.println(monsters)
}
//输出:[map[age:500 name:牛魔王] map[age:400 name:玉兔精] map[age:1500 name:新的妖怪-孙悟空]]

map排序

go中没有一个专门的方法针对map的key进行排序

go中的map默认是无序的,注意也不是按照添加的顺序存放的,每次遍历,得到的输出可能不一样

go中map的排序,是先将key进行排序,然后根据key值遍历输出即可

案例演示

package main

import (
   "fmt"
   "sort"
)
func main()  {
   //map的排序
   map1 := make(map[int]int,10)
   map1[10] = 100
   map1[1] = 13
   map1[4] = 56
   map1[8] = 90
   fmt.println(map1)
   //如果按照map的key的顺序进行排序输出
   //1. 先将map的key放入到切片中
   //2. 对切片排序
   //3. 遍历切片,然后按照key来输出map的值
   var keys []int
   for k, _ := range map1 {
      keys = append(keys, k)
   }
   //排序
   sort.ints(keys)
   fmt.println(keys)
   for _, k := range keys {
      fmt.printf("map1[%v] = %v \n", k, map1[k])
   }
}
//输出:map[1:13 4:56 8:90 10:100]
//[1 4 8 10]
//map1[1] = 13 
//map1[4] = 56 
//map1[8] = 90 
//map1[10] = 100 

map使用细节

map是引用类型,遵守引用类型传递的机制,在一个函数接收map,修改后,会直接修改原来的map

func modify(map1 map[int]int)  {
   map1[10] = 900
}
func main()  {
   map1 := make(map[int]int)
   map1[1] = 90
   map1[2] = 88
   map1[10] = 1
   map1[20] = 2
   modify(map1)
   fmt.println(map1)
}
//输出:map[1:90 2:88 10:900 20:2]

map的容量达到后,再想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态的增长 键值对(key-value)

map的value也经常使用struct类,更适合管理复杂的数据(比前面value是一个map更好),比如value为student结构体

type stu struct {
	name  string
	age   int
	address  string
}

func main()  {
	//3)map的value也经常使用struct类型,
	// 更适合管理复杂的数据(比前面value是一个map更好),
	// 比如value为student结构体
	//1. map 的 key 为学生的学号,是唯一的
	//2. map的value 为结构体,包含学生的名字,年龄,地址
	students := make(map[string]stu, 10)
	//创建2个学生
	stu1 := stu{"tom", 18, "北京"}
	stu2 := stu{"mary", 28, "上海"}
	students["no1"] = stu1
	students["no2"] = stu2

	fmt.println(students)
	//遍历各个学生信息
	for k, v := range students {
		fmt.printf("学生的编号是%v \n",k)
		fmt.printf("学生的名字是%v \n",v.name)
		fmt.printf("学生的年龄是%v \n",v.age)
		fmt.printf("学生的地址是%v \n",v.address)
	}
}
//map[no1:{tom 18 北京} no2:{mary 28 上海}]
//学生的编号是no1
//学生的名字是tom
//学生的年龄是18
//学生的地址是北京
//学生的编号是no2
//学生的名字是mary
//学生的年龄是28
//学生的地址是上海 

map练习题

演示一个key-value的value是map的案例

​ 比如:要存放3个学生信息,每个学生有name和sex信息

​ 思路: map[string]map[string]string

func main()  {
   studentmap := make(map[string]map[string]string)
   studentmap["stu01"] = make(map[string]string, 3)
   studentmap["stu01"]["name"] = "tom"
   studentmap["stu01"]["sex"] = "男"
   studentmap["stu01"]["address"] = "北京长安街"

   studentmap["stu02"] = make(map[string]string, 3)  //这句话不能少!!!
   studentmap["stu02"]["name"] = "mary"
   studentmap["stu02"]["sex"] = "女"
   studentmap["stu02"]["address"] = "上海黄埔江"

   fmt.println(studentmap)  //map[stu01:map[address:北京长安街 name:tom sex:男] stu02:map[address:上海黄埔江 name:mary sex:女]]
   fmt.println(studentmap["stu02"])  //map[address:上海黄埔江 name:mary sex:女]
   fmt.println(studentmap["stu02"]["address"])  //上海黄埔江
}

编写一个函数modifyuser(users map[string]map[string]string,name string)完成上下述功能

  1. 使用map[string]map[string]string的map类型

  2. key:表示用户名,是唯一的,不可以重复

  3. 如果某个用户存在,就将其密码修改"888888",如果不存在就增加这个用户信息(包括昵称nickname和密码pwd)。

func modifyuser(users map[string]map[string]string,name string)  {
   //判断users中是否有name
   //v,ok := users[name]
   if users[name] != nil {
      //有这个用户
      users[name]["pwd"] = "888888"
   } else {
      //没有这个用户
      users[name] = make(map[string]string,2)
      users[name]["pwd"] = "888888"
      users[name]["nickname"] = "昵称~" + name //示意
   }
}
func main()  {
   users := make(map[string]map[string]string,10)
   users["smith"] = make(map[string]string,2)
   users["smith"]["pwd"] = "999999"
   users["smith"]["nickname"] = "小花猫"

   modifyuser(users,"tom")
   modifyuser(users,"mary")
   modifyuser(users,"smith")
   fmt.println(users)
}
//输出:map[mary:map[nickname:昵称~mary pwd:888888] smith:map[nickname:小花猫 pwd:888888] tom:map[nickname:昵称~tom pwd:888888]]