Go核心开发学习笔记(廿) —— 二维数组,map(映射)

二维数组概念:一维数组中的元素又是一个一维数组。

var arr [4][6]int // [ [0 0 0 0 0 0] [0 0 0 0 0 0] [0 0 0 0 0 0] [0 0 0 0 0 0] ]
arr [1][2] = 1
arr [2][3] = 2
arr [3][4] = 3
fmt.Println(arr) // [[0 0 0 0 0 0] [0 0 1 0 0 0] [0 0 0 2 0 0] [0 0 0 0 3 0]]

二维数组内存分析:
[4][6]这个矩阵一共24个int元素,每个int元素在x64计算机内占8bytes,所以这个二维数组占据24*8也就是192Bytes的空间。
由于4个6元素数组构成矩阵,每6个元素为一个数组的首地址一定和a[i]的内存地址是一样的,即为 &a[i] == &a[i][0]。

二维数组遍历

package main
import "fmt"
func main() {
	/*
	示例,初始化一个二维数组
	 */
	//写法可以省略,var arr [2][3]int = [2][3]int{{1,2,3},{4,5,6}}
	var arr = [2][3]int{{1,2,3},{4,5,6}}
	fmt.Println(arr)
	//传统遍历
	for i:=0 ; i < len(arr) ; i++ {                 //替换为len(arr)
		for j := 0 ; j < len(arr[i]) ; j++ {        //替换为len(arr[i])
			fmt.Printf("%d ",arr[i][j])
		}
	fmt.Println()
	}
	// for-range 遍历
	for i ,value1 := range arr {
		for j , value2 := range value1 {
			fmt.Printf("a[%v][%v] = %v\n",i,j,value2)
		}
	}
}

map的声明

声明:
var <map名字> map[<key的数据类型>]<value的数据类型>
例:
var testmap map[int]int
var testmap1 map[int]string
var testmap2 map[string]string
var testmap3 map[string]map[string]string //定义一个字符串类型的map,每个key对应的values,又是一个字符串类型的map,相当于嵌套。

key:多种类型,布尔,int,float,string,ptr,channel,一般用的最多的是int和string,其他用法很奇怪。
slice,func,map是不可以作为key的,因为无法使用 "=="来判断。

values: 可以是int,string,float,struct,map(见例3)

★★★与数组类型不一样,map的声明是不会分配内存的,初始化需要make(),分配内存后才能赋值和使用,不是默认值为0,"",false等,map与数组不同,是无序的。
类似python中的字典,字典嵌套字典,且无序

示例1:

package main
import "fmt"
func main() {
	var testmap map[string]string
	fmt.Println(testmap)
}         //map[] 这个是反馈结果

示例2:

package main
import "fmt"
func main() {
	var testmap map[string]string
	testmap["name"] = "durant"
	fmt.Println(testmap)
}         //panic: assignment to entry in nil map 因为没有分配内存

示例3:

package main
import "fmt"
func main() {
	var testmap map[string]string
	testmap = make(map[string]string,10)
	testmap["name"] = "durant"
	fmt.Println(testmap)
}         //使用make函数分配一个10对kv的map,10这里不传参数默认为1,注意写法  返回结果map[name:durant]

示例4:

package main
import "fmt"
func main() {
	var testmap map[string]string
	testmap = make(map[string]string,10)
	testmap["name"] = "durant"
	testmap["name"] = "james"
	testmap["name1"] = "james"
 	fmt.Println(testmap)
}          //一个map中key只能唯一,后面你会覆盖前面的;不同的key可以有同一个值 返回结果为map[name:james name1:james]

map的使用方式:

1. 按照上述使用make()对map进行声明,然后赋值:
var testmap map[string]string
testmap = make(map[string]string,10)
testmap[“key”] = “values”

2. 直接简化上述,直接声明,然后赋值:
var testmap = make(map[string]string,10)
testmap[“key”] = “values”

3. 简化上述两版,不使用make(),直接声明后赋值:
var testmap = map[string]string{“key”:“values”,“key1”:“values1”}
testmap[“key2”] = “values2”

案例:三个球员编号为001,002,003,每个编号可以查询他们的姓名和球衣号码,每个学生名字为 durant james curry,球衣号码35,23,30
下面为简略写法,大同小异,取值都是通过 [key] —> [key][key] 这种方式取值。

package main
import "fmt"
func main() {
	var nba = map[string]map[string]string{"001":{"name":"durant","num":"35"}}
	fmt.Println(nba)                                  //map[001:map[name:durant num:35]]
	fmt.Println(nba["001"])                           //map[name:durant num:35]
	fmt.Println(nba["001"]["name"])                   //durant
}

map的CRUD
1. map的增加和更新: map[key] = value ,如果key不存在就增加©,如果key存在就是修改(U)。
见下面示例

	package main
	import "fmt"
	func main() {
		var nba = map[string]map[string]string{"001":{"name":"durant","num":"35"}}
		nba["001"]["name"] = "james"
		nba["002"] = make(map[string]string,2)            //分配新的内存空间给map中key对应的值map
		nba["002"]["name"] = "durant"
		nba["002"]["num"] = "35"
		fmt.Println(nba)                                  //map[001:map[name:durant num:35]]
		fmt.Println(nba["001"])                           //map[name:durant num:35]
		fmt.Println(nba["001"]["name"])                   //james
		fmt.Println(nba["002"]["name"])                   //durant
	}

2. map的删除:使用delete(),使用方式为 delete(<map>,“001”)

	package main
	import "fmt"
	func main() {
		var nba = map[string]map[string]string{"001":{"name":"durant","num":"35"}}
		nba["001"]["name"] = "james"
		nba["002"] = make(map[string]string,2)
		nba["002"]["name"] = "durant"
		nba["002"]["num"] = "35"
		delete(nba["001"],"name")                         //删除二层key
		delete(nba,"002")                                 //删除一层key
		delete(nba,"003")                                 //不存在003的,不会报错,什么也不操作
		fmt.Println(nba)                                  //map[001:map[num:35]]
		fmt.Println(nba["001"])                           //map[num:35]
		fmt.Println(nba["001"]["name"])                   //不存在
		fmt.Println(nba["002"]["name"])                   //不存在
		nba = make(map[string]map[string]string)
		fmt.Println(nba)                                  //删除其中所有的key太麻烦,直接指向空map
	}

细节:golang中没有删除所有key的函数,只能使用遍历,然后for删除;让map指向一个新的make(),让原来的成为垃圾,被gc回收。

3. map的查找:

package main
import "fmt"
func main() {
	var testmap map[string]string
	testmap = make(map[string]string,10)
	testmap["name"] = "james"
 	fmt.Println(testmap)
	dest_key , value := testmap["shit"]        //如果要找一个key是否存在,可以使用if <value> { "打印出对应的key",key}
	if value {
		fmt.Println("这个key存在")                         
	} else {
		fmt.Println("没找到这个key")
	}

map的遍历:

由于map的数据类型复杂,所以不能使用单纯for进行遍历,只能使用for-range
这个完全类似python的字典遍历,for k,v in xxx.items(): …

一层map:

package main
import "fmt"
func main() {
	var testmap = map[string]string{"name1":"durant","name2":"james"}
	for k,v := range testmap{
		fmt.Printf("%v %v\n",k,v)
	}
}

//对比下python的字典
dict = {"name1":"durant","name2":"james"}
for k,v in dirt.items()
	print("%s %s\n",k,v)

二层map,也就是一层map的值是map,<一层map>[<key>][<二层key>]

package main
import "fmt"
func main() {
	var testmap = map[string]map[string]string{"thunder":{"name1":"durant"},"lackers":{"name2":"james"}}
	for k1,v1 := range testmap {                 //先把一层的k,v拿出来,"二层的map" == "一层的v"
		fmt.Printf("%v %v\n",k1,v1)              //thunder map[name1:durant];lakers map[name2:james]
		for k2,v2 := range v1 {                  //内层循环要拿v1这个map的值,里面再对应k2,v2
			fmt.Printf("%v %v\n",k2,v2)          //name1 durant;name2 james   其实就是注意层级关系
		}
	}
	
	//map的长度统计:
	len(<map_name>) 得到的结果是里面包含多少个kv对。
	fmt.Println(len(testmap))                    //2个kv对 雷霆和湖人
	fmt.Println(len(testmap["thunder"]))         //1个kv对 球员姓名
}

map的切片与添加元素

使用map切片,这样使用则map个数可以动态变化,不用再考虑kv对限制了。

案例演示:使用map记录NBA球员的姓名和年龄,要求可以根据需求动态增加球员信息,球员数不定。

package main
import "fmt"
func main() {
	/*
	使用map记录不同球队的NBA球员的姓名和年龄,要求可以根据需求动态增加球员信息,球员数不定。
	 */
	var nbaPlayer []map[string]string
	nbaPlayer = make([]map[string]string,2)          //切片类型首先要用make分配内存

	if nbaPlayer[0] == nil {
		nbaPlayer[0] = make(map[string]string,2)     //继续为map类型分配内存,所以一共需要分配两次内存
		nbaPlayer[0]["name"] = "durant"
		nbaPlayer[0]["age"] = "30"
	}
	if nbaPlayer[1] == nil {
		nbaPlayer[1] = make(map[string]string,2)     //继续为map类型分配内存,所以一共需要分配两次内存
		nbaPlayer[1]["name"] = "james"
		nbaPlayer[1]["age"] = "35"
	}

	//由于上限设置为2,所以kv对只能有两个,如果现在写nbaPlayer[2]一定会报错out of range
	//所以使用append()动态添加,先定义一个新的player,然后添加到前面就可以了
	add_nbaPlayer := map[string]string{"name":"curry","age":"29"}       //添加一个新球员,用另外一个map
	nbaPlayer = append(nbaPlayer,add_nbaPlayer)                         //append()需要赋值给原切片
	/*
	追加结果[map[age:30 name:durant] map[age:35 name:james] map[age:29 name:curry]]
	 */
	fmt.Println(nbaPlayer)
}

map的排序

  1. map本身作为字典就是无序的。
  2. golang中没有针对map排序的方法。
  3. 想排序先对map中的key进行排序,然后根据key值遍历输出即可。
  4. 排序方法:map中所有的key放入切片中,对切片进行排序,遍历切片,按照key来输出map的值
	package main
	import (
		"fmt"
		"sort"
	)
	func main() {
		/*
		1. map本身作为字典就是无序的。
		2. golang中没有针对map排序的方法。
		3. 想排序先对map中的key进行排序,然后根据key值遍历输出即可。
		4. 排序方法:map中所有的key放入切片中,对切片进行排序,遍历切片,按照key来输出map的值
		*/
		var nbaPlayers = map[int]string{30:"curry",23:"james",35:"durant",1:"mcgrady"}
		fmt.Println(nbaPlayers)
		//这样每次输出都是无序的,把所有的key添加到一个切片中
		var slice_key []int
		//使用for-range方式遍历map,取出keys和values
		for keys, _ := range nbaPlayers {
			slice_key = append(slice_key,keys)
		}
		//使用sort.Ints([]int)方法,对int型切片进行递增排列
		sort.Ints(slice_key)                   //这个方法解决了冒泡的问题,如果从大到小排列则只需用转置方法来解决
		fmt.Println(slice_key)

		//这时key已经成功取出成为切片,那么接下来要遍历切片,取出所有的value值作为map nbaPlayer的key
		for _ , v := range slice_key{
			fmt.Println(nbaPlayers[v])
		}
	}

map使用细节

  1. 引用类型数据,函数接收map修改后,调用后会直接改变原map值。

    package main
    import "fmt"
    func modify(test map[int]int) {
    	test[2] = 6                    //下标为2的数值改为6
    }
    func main() {
    	var test map[int]int
    	test = make(map[int]int,3)
    	test[0] = 3
    	test[1] = 4
    	test[2] = 5
    	modify(test)
    	fmt.Println(test)             //map[0:3 1:4 2:6] 不再是5,直接改成6了
    }
    
  2. map的容量会自动扩容,不会发生panic。

  3. map的value也经常使用结构体struct,比map更好,结构体里面可以包含不同的数据类型。

示例1:

package main
import "fmt"
func modifyUser(users map[string]map[string]string, name string,nickName string) {
	if users[name] != nil {
		users[name]["pwd"] = "888888"
	} else {
		users[name] = make(map[string]string,2)         //嵌套map需要make()两次,外层内层都需要make()
		users[name]["nickName"] = nickName
		users[name]["pwd"] = "111111"
	}
}

func main() {
	/*
	需求:某个用户名存在,将其密码修改为"888888";
	      不存在则创建用户,包含信息nickName和pwd
	 */
	var users map[string]map[string]string
	users = make(map[string]map[string]string,2)
	modifyUser(users,"Jimmy","jim")
	fmt.Println(users)
	modifyUser(users,"Jimmy","jim")
	fmt.Println(users)
}
发布了49 篇原创文章 · 获赞 18 · 访问量 4008

猜你喜欢

转载自blog.csdn.net/weixin_41047549/article/details/89931693