Go經常使用的數據結構

閒着無事,隨便寫寫,初學Go,望各位大神輕噴!Go自帶的幾個複合數據類型,基本數據類型咱就不說了,大部分語言常見的幾種複合數據類型大概有數組、字典、對象等,不一樣語言叫法不同,用法也有差別,好比說PHP裏面數組其實嚴格來講不算數組。json

1.數組

Go裏面的數組和C相似,是由有序固定長度特定類型元素組成。畫重點,固定長度和特定類型。在不少弱類型的語言裏面,數組很是隨意,PHP的數組本質上是一個hash table,和C的數組差別太大,因此寫慣了PHP再寫Go的話這點須要注意。數組

基礎用法1:

package main

import "fmt"

func main() {
	var a [5]int

	a[1] = 1
	a[2] = 3

	var b [10]string

	b[0] = "a1"
	b[1] = "b2"
	b[2] = "c5"

	fmt.Printf("%v\n", a)
	fmt.Printf("%v\n", b)
}
---結果---
[0 1 3 0 0]
[a1 b2 c5       ]
複製代碼

從語法上看,Go定義數組的類型放在後面,這點寫慣C系語言的估計蛋疼。數組也是經過索引下標訪問,若是不初始化賦值的話,默認狀況下,int類型的元素是0,string類型是空字符串。bash

基礎用法2

咱們也能夠不先定義,直接使用字面量初始化數組:app

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4, 5, 7}

	fmt.Printf("%v", a)
}
---結果---
[1 2 3 4 5 7]
複製代碼

在這種狀況下,咱們能夠省略長度,使用3個點代替,編譯器會自動判斷。函數

數組遍歷

主要有兩種方式:ui

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4, 5, 7}

	for i := 0; i < len(a); i++ {
		fmt.Print(a[i])
	}
	
	for k, v := range a {
		fmt.Print(k, "->", v)
	}
}
複製代碼

若是知道長度的話可使用for循環,不然可使用for range 這種語法。this

數組函數

Go內置了一些函數能夠操做數組,若是你使用了IDE的話,能夠「點」出來:spa

然而,append並非用來操做數組的,其實它是用來操做變長數組的,即slice, 又稱切片。3d

2.Slice(切片)

傳統的數組長度固定,因此實際用途並很少,除非你明確知道本身想要多長的數組,不少時候咱們須要的是一個能夠改變長度大小的數組,在Go裏面這類型被稱爲切片。code

slice實際上是從數組而來的,它和數組很是像,區別就在於slice沒有固定長度,很是方便,因此平時通常都是用這個比較多。

基礎用法1:

package main

import "fmt"

func main() {
	var a []int

	a = append(a, 2)
	a = append(a, 1)
	a = append(a, 4)
	a = append(a, 5)

	fmt.Printf("%v", a)
}
複製代碼

區別就在於slice在定義的時候不須要指定長度,也不用3個點,可是這就意味着你不能使用索引下標的方法去賦值了,可使用append函數去追加元素。

並且在使用slice的也須要注意下標,若是大於slice的長度也會出現 panic: runtime error: index out of range

基礎用法2

package main

import "fmt"

func main() {
	a := [...]int{1,2,3,4,5,6,7,8}

	s1 := a[0:]

	s2 := a[1:5]

	s3 := a[4:6]

	fmt.Printf("%v\n", a)
	fmt.Printf("%v\n", s1)
	fmt.Printf("%v\n", s2)
	fmt.Printf("%v\n", s3)
}
複製代碼

slice可使用[start:end]這種語法從一個數組裏面生成,好比a[1:5]意思是生成一個包含數組索引1到5的之間元素的slice。

在Go裏面不一樣長度可是同一類型的數組是不一樣類型的,好比你定義了2個int數組,一個長度爲5,一個長度爲10,他們其實並非同一個類型,雖然都是int類型。cannot use a (type [10]int) as type [5]int in argument

因此在大部分時候咱們須要的是一個slice,並非一個數組。雖然這個2個用法基本上一毛同樣。。。

3.Map

在不少語言裏面,map被叫做字典,這個中文名稱很親切,字典就是一種key value結構,小時候你們都用過新華字典,字典的特徵就是每個字都對應一個解釋。可是Go的map是無序的,這點你們須要注意。若是有童鞋寫過PHP,會發現這個數據類型相似PHP裏面的關聯數組。

在Go裏面,它和slice的區別就是slice的索引是數值,map的索引類型就豐富了,基本上經常使用數據類型都支持,甚至包括結構體。

基礎用法

和其它數組類型同樣,map也支持先定義後賦值,或者直接使用字面量建立。可是若是使用先定義後賦值這種方式,map須要使用make初始化。

package main

import "fmt"

func main() {
	var m1 map[string]string

	m1 = make(map[string]string)

	m1["name"] = "Golang"
	m1["address"] = "BeiJin"

	m2 := map[string]string{
		"name": "GoLand",
		"addr": "ShangHai",
	}

	fmt.Printf("%v\n", m1)
	fmt.Printf("%v", m2)
}
---結果---
map[name:Golang address:BeiJin]
map[name:GoLand addr:ShangHai]
複製代碼

map可使用for range 語法遍歷,可是須要注意的是每次遍歷的順序是無序的。

如何判斷一個key是否存在map裏面?在PHP裏面咱們有一個array_key_exists函數,在Go裏面寫法略有不一樣:

age, ok := m1["age"]
if !ok {
    fmt.Println("age 不存在", age)
}
複製代碼

其實若是你不判斷是否存在直接取也能夠,並不會報錯,只不過獲取到的值是一個對應類型的零值。

4.結構體

Go的結構體也相似C,相似於如今不少面向對象的語言裏面的類,每每用來存儲一組相關聯的數據,Go雖然不是一個徹底面向對象的語言,可是使用結構體能夠實現相似效果。

基本用法

package main

import "fmt"

type Goods struct {
	name    string
	price   int
	pic     string
	address string
}

func main() {
	var goods Goods
	goods.name = "商品1"
	goods.price = 100
	goods.pic = "http://xxxx.jpg"
	goods.address = "中國"

	fmt.Printf("%v\n", goods)

	goods2 := Goods{
		name:    "商品2",
		price:   200,
		pic:     "http://xxxx.png",
		address: "日本",
	}

	fmt.Printf("%v", goods2)
}
---結果---
{商品1 100 http://xxxx.jpg 中國}
{商品2 200 http://xxxx.png 日本}
複製代碼

先定義後賦值或者字面量賦值均可以,值得一提的是在Go裏面若是結構體或者其屬性的首字母大寫則表示該結構體或者屬性能夠被導出,也就是被其它包使用。結構體裏面的屬性成員的類型也能夠是結構體,這就變相實現了類的繼承。

既然結構體和類差很少,那類的方法在哪裏定義呢?這點Go實現的就比較巧妙了!

func (g Goods) getName() string {
	return g.name
}
複製代碼

咱們只須要在函數的前面放一個變量,就變成了方法。在不少語言裏面,函數和方法區分不是很明顯,大部分時候咱們都是混着叫,可是在Go裏面,方法指的是針對某一類型的函數。好比在上面的例子裏面,這個getName函數就是針對Goods結構體的,用面向對象的說法就是一個類方法。因此咱們可使用 goods.getName()的形式調用這個方法。

上面的代碼裏那個附加的參數p,叫作方法的接收器(receiver),早期的面嚮對象語言留下的遺產將調用一個方法稱爲「向一個對象發送消息」。 在Go語言中,咱們並不會像其它語言那樣用this或者self做爲接收器;咱們能夠任意的選擇接收器的名字。因爲接收器的名字常常會被使用到,因此保持其在方法間傳遞時的一致性和簡短性是不錯的主意。這裏的建議是可使用其類型的第一個字母。

在Go裏面咱們能夠爲任何類型定義方法,不管是常見的int、string,仍是map、struct都沒問題,下面的例子裏面就是爲int類型擴展一個方法:

package main

import "fmt"

type MyInt int

func main() {
	myInt := MyInt(10)
	res := myInt.add(100)

	fmt.Printf("%d", res)
}

func (m MyInt) add(a int) int {
	return int(m) + a
}
---結果---
110
複製代碼

咱們沒法直接使用基本數據類型,可是咱們能夠起一個別名,純屬娛樂!

5.JSON

嚴格來講,JSON並非一種數據類型,可是json是如今最流行的數據交換格式,Go對json的支持也很好,在Go裏面主要經過結構體生成json,咱們也能夠把一個json轉換成結構體。

package main

import (
	"encoding/json"
	"fmt"
)

type Goods struct {
	Name    string
	Price   int
	Address string `json:"address2"`
	Tag     string
}

func main() {
	goods := Goods{
		"商品1", 100, "中國", "特價",
	}

	bytes, err := json.Marshal(goods)

	if err != nil {
		panic(err)
	}

	fmt.Printf("%s", bytes)
}
---結果---
{"Name":"商品1","Price":100,"address2":"中國","Tag":"特價"}
複製代碼

把結構體轉換成json可使用Marshal方法,有一點須要注意: 結構體的屬性成員首字母必須大寫,可是可使用註解的Tag標註轉換成json以後的key名稱。

json字符串轉換成結構體步驟差很少:

package main

import (
	"encoding/json"
	"fmt"
)

type Goods struct {
	Name    string
	Price   int
	Address string `json:"address2"`
	Tag     string
}

func main() {
	jsonStr := `{"Name":"商品1","Price":100,"address2":"中國","Tag":"特價"}`

	goods := Goods{}

	err := json.Unmarshal([]byte(jsonStr), &goods)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%v", goods)
}
---結果---
{商品1 100  特價}
複製代碼

這在咱們平時寫接口或者請求接口的時候很是好使,簡單易用!

好了,今天就介紹這麼多了,謝謝你們查看!

相關文章
相關標籤/搜索