Go中的map和指針


本文參考:https://www.liwenzhou.com/posts/Go/08_map/

MAP(映射)

Go語言中提供的映射關系容器為map,其內部使用散列表(hash)實現。(類似於Python中的字典dict)

映射概述

map是一種無序的基於key-value的數據結構,Go語言中map是引用類型,必須初始化后才能使用。

創建map

Go語言中map的定義語法如下:

map[keyType]valueType

//keyType 表示鍵的類型
//valueType表示鍵對應值的類型

map類型的變量默認初始值為nil,需要使用make()函數來分配內存。

make(map[keyType]ValueType,cap)

// cap表示map的容量,不指定時默認為0,但是我們應該在初始化map的時候就為其指定一個合適的容量

示例:

func main(){
	// 在聲明的時候填充元素
	userInfo := map[string]string{
		"username":"Negan"
		"password":"mima"
	}
	
	// 使用內建函數make()創建
	scoreMap:=make(map[string]int,8)
	scoreMap["張三"] = 90
	scoreMap["李四"] = 100
	fmt.Println(scoreMap)   //map[張三:90 李四:100]
	fmt.Println(scoreMap["李四"]) //100
	fmt.Printf("type of a:%T\n",scoreMap) //type of a:map[string]int
    
    //創建一個string為鍵,值為任意類型的map
    userMap := make(map[string]interface{}) 
    userMap["name"] = "Negan"
    userMap["age"] = 68
}

判斷某個鍵是否存在

Go語言中有個判斷map中鍵是否存在的特殊寫法

value,ok:=map[key]

示例:

func main(){
	scoreMap := make(map[string]int)
	scoreMap["張三"] = 90
	scoreMap["李四"] = 100
	// 如果key存在ok為true,v為對應的值,不存在ok為false,v為值類型的零值
	v,ok := scoreMap["張三"]
	if ok{
		fmt.Println(v)
	}else{
		fmt.Println("查無此人")
	}
}

map的遍歷

range關鍵字對map遍歷,可以同時得到鍵值對的key和value,也可僅僅得到key

func main(){
	scoreMap := make(map[string]int)
	scoreMap["張三"] = 90
	scoreMap["李四"] = 100
	// 遍歷key和value
	for k,v := range scoreMap{
		fmt.Println(k,v)
	}
	//僅僅遍歷key
	for k := range scoreMap{
		fmt.Println(k)
	}
}
  • 注意:遍歷map時的元素順序與添加鍵值對的順序無關。

按照指定循序遍歷map

func main(){
    rand.Seed(time.Now().UnixNano())  // 初始化隨機數種子
    
    scoreMap := make(map[string]int,200)
    
    for i:=0;i<100;i++{
        key := fmt.Sprintf("stu%02d",i)  // 生成stu開頭的字符串
        value := rand.Intn(100)  // 生成0-99的隨機整數
        scoreMap[key] = value
    }
    // 取出map中所有的key存放到切片keys中
    keys := make([]string,0,200)
    for key:=range scoreMap{
        keys = append(keys, key)
    }
    // 對切片進行排序
    sort.Strings(keys)
    // 按照排序后的key遍歷map
    for _,key := range keys{
        fmt.Println(key, scoreMap[key])
    }
}

使用delete()函數刪除鍵值對

使用delete()內建函數從map中刪除一組鍵值對,delete()函數的格式如下:

delete(map,key)

// map:表示要刪除鍵值對的map
// key:表示要刪除的鍵值對的鍵

示例:

func main(){
	scoreMap := make(map[string]int)
	scoreMap["張三"] = 90
	scoreMap["李四"] = 100
	
	delete(scoreMap,"張三")  // 將張三:90從map中刪除
	fmt.Println(scoreMap)
}

元素為map類型的切片

下面代碼延時了切片中的元素為map類型時的操作:

func main(){
	var mapSlice = make([]map[string]string,3)
	for index,value := range mapSlice{
		fmt.Printf("index:%d value:%v\n",index,value)
	}
	fmt.Println("after init")
	// 對切片中的map元素進行初始化
	mapSlice[0] = make(map[string]interface{},10)
	mapSlice[0]["name"] = "Negan"
	mapSlice[0]["age"] = 68
	mapSlice[0]["hobby"] = "棒球"
    
    for index,value:= range mapSlice{
        fmt.Printf("index:%d value:%v\n",index, value)
    }
}

值為切片類型的map

下面代碼演示map中值為切片類型的操作

func main{
	sliceMap := make(map[string][]string,3)
	fmt.Println(sliceMap)
	fmt.Println("after init")
	key := "中國"
	value,ok := sliceMap[key]
	if !ok{
		value = make([]string,0,2)
	}
	value = append(value, "北京","上海","西安")
	sliceMap[key = value
	fmt.Println(sliceMap)
}

練習題

1、寫一個程序,統計一個字符串中每個單詞出現的次數。比如:“how do you do”中how=1 do=2 you=1。

func main(){
    s := “how do you do”
    s_sclice := strings.Split(s," ")  // 分割字符串
    
    count_map := make(map[string]int)
    for key := range s_sclice{
        _,ok:=count_map[key]
        if !ok{
            count_map[key] = 1
        }else{
            count_map[key] += 1
        }
    }
    fmtPrintln(count_map)
}

指針

Go中的指針區別於C/C++中的指針,Go語言中的指針不能進行便宜和運算,是安全指針。

任何數據載入內存后,在內存都有它們的地址,這就是指針。為了保存一個數據在內存中的地址,我們就需要指針變量。指針變量的值就是內存中的一塊地址編號。

比如:“卑鄙是卑鄙者的通行證,高尚是高尚的墓志銘”,想把它寫入程序中,程序一啟動,這句話是要加載到內存(假設內存地址ox123456),在程序中把這句詩賦值給變量A,把內存地址賦值給變量B。這時候變量B就是一個指針變量。通過變量AB都能找到這句詩。

Go語言中的指針不能進行偏移和運算,因此Go語言中的指針操作非常簡單,只需要記住兩個符號,&(取地址)和*(根據地址取值)

指針地址和指針類型

每個變量在運行時都擁有一個地址,這個地址代表變量在內存中的位置。

Go語言中使用&字符放在變量前面對變量進行“取地址操作”。Go語言中值類型(int、float、bool、string、array、struct)都有對應的指針類型。

*int*int64*string等。

取變量指針語法如下:

ptr := &v // v的類型為T

// v:代表被取地址的變量,類型為T
// ptr:用於接收地址的變量,ptr的類型就是*T,稱為T的指針類型。

示例:

func main(){
	a := 10
	b := &a
	fmt.Printf("a:%d ptr:%p\n", a, &a)  //a:10 ptr:0xc00001a078
	fmt.Printf("b:%p type:%T\n", b, b)  //b:0xc00001a078 type:*int
	fmt.Println(&b)   // 0xc00000e018
}

指針取值

在對普通變量使用&操作符取地址后獲得這個變量的指針,然后可以對指針使用*操作。也就是指針取值。

func main(){
	// 指針取值
	a := 10
	b := &a   // 取變量a的地址,將指針保存到b
	fmt.Printf("type of b:%T\n",b)   // type of b:*int
	c := *b  // 指針取值(根據指針去內存取值)
	fmt.Printf("type of c:%T\n", c)   // type of c:int
	fmt.Printf("value of c:%v\n", c)  // value of c:10
}

總結:取地址操作符&和取值操作符*是一對互補操作符。

&取出地址,*根據地址取出地址指向的值

變量、指針地址、指針變量、取地址、取值的相互關系和特性如下:

  • 對變量進行取地址(&)操作,可以獲取這個變量的指針變量
  • 指針變量的值是指針地址
  • 對指針變量進行取值(*)操作,可以獲得指針變量指向的原變量的值。

指針傳值示例

func modify1(x int){
	x = 100
}

func modify2(x *int){
	*x = 100
}

func main(){
	a := 10
	modify1(a)
	fmt.Println(a)   // 10 
	modify2(&a)
	fmt.Println(a)   // 100  
}

new和make

在Go語言中對於引用類型的變量,在使用時不僅要聲明它,還要為它分配內存空間,否則我們的值沒辦法進行存儲,而對於值類型的聲明,不需要分配內存空間,是因為它們在聲明的時候已經默認分配好了內存空間。

func main(){
	var a *int
	*a = 100
	fmt.Println(*a)   // 引發panic
	
	var b map["string"]int
	b["age"] = 10
	fmt.Println(b)   // 引發panic
}

new

new是一個內置函數,函數簽名如下:

func new(Type) *Type

// Type 表示類型,new函數只接受一個參數,這個參數是一個類型
// *Type 表示類型指針,new函數返回一個指向該類型內存地址的指針

new函數不太常用,使用new函數得到的是一個類型的指針,並且該指針對應的值為該類型的零值。

func main(){
	a := new(int)
	b := new(bool)
	fmt.Prtintf("%T\n",a)  // *int
	fmt.Printf("%T\n", b)  // *bool
	fmt.Printf(*a)  // 0
	fmt.Printf(*b)  //false
}

剛開始我們的實例代碼中var a *int只是聲明一個指針變量a,但是並沒有初始化,指針座位引用類型,需要初始化后才能擁有內存空間,才可以給它進行復制。

func main(){
	var a *int   // 聲明a為int類型的指針
	a = new(int)  // 分配內存空間
	*a = 10
	fmt.Println(*a)
}

make

make也是用來分配內存的,區別於new,它只用於slice,map以及chan的內存創建。而且返回的類型就是這三個類型的本身。而不是它們的指針類型。因為這三種類型本身就是引用類型。所以沒必要返回它們的指針類型。

func make(t Type, size ...IntegerType) Type

make函數是無可替代的,我們在使用slice、map以及channel的時候,都需要使用make進行初始化,然后才可以對它們進行操作。

剛開始的示例中var b map[string]int只是生命變量b是一個map類型的變量。不能直接進行給它復制,需要使用make函數進行操作之后,才能對其進行鍵值對賦值。

func main(){
	var b map[string]int
	b = make(map[string]int,10)
	b["age"] = 10
	fmt.Println(b)
}

new和make的區別

兩者都是用來做內存分配的。

make只用於slice、map、以及channel的初始化,返回的還是這三個引用類型本身

new用於類型的內存分配,並且內存對應的值為類型零值,返回的是指向類型的指針。

本文參考:https://www.liwenzhou.com/posts/Go/08_map/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM