Golang當中有三種初始化的方法,
var v2 int = 10
var v3 = 10
v4 := 10
var 變量名 類型 = 表達式
編譯器推導類型的格式
在標准格式的基礎上,將 int 省略后,編譯器會嘗試根據等號右邊的表達式推導 hp 變量的類型。
var hp = 100
等號右邊的部分在編譯原理里被稱做右值(rvalue)。
短變量聲明並初始化
var 的變量聲明還有一種更為精簡的寫法,例如:
hp := 100
如果 hp 已經被聲明過,但依然使用:=時編譯器會報錯,代碼如下:
no new variables on left side of :=
意思是,在“:=”的左邊沒有新變量出現,意思就是“:=”的左邊變量已經被聲明了。
一、數組初始化方式
- var [length]Type
var array [5]int //這種方式,只是初始化,不帶初始化值,數組長度,已經定義好, 但是其實初始化的值,已經有了並且是該類型的最小值(bool false),int 0, string ' ' 其他,自行驗證
- var [N]Type{value1, value2, ... , valueN}
var array = [5]int{1, 2, 3, 4, 5} // 這種方式,既初始化變量,又帶了初始化值,數組長度已經定義好
Initializing an array using an array literal
You can declare and initialize an array at the same time like this -
// Declaring and initializing an array at the same time
var a = [5]int{2, 4, 6, 8, 10}
- var [...]Type{value1, value2, ... , valueN}
var array = [...]int{1, 2, 3, 4, 5} // 這種方式,既初始化變量,也是帶了初始值,數組長度,根據初始值的個數而定,也就是五個
// Letting Go compiler infer the length of the array
a := [...]int{3, 5, 7, 9, 11, 13, 17}
- :=[N]Type{value1, value2, ... , valueN}
array :=[5]int{1, 2, 3, 4, 5} // 這種方式,省去 var 關鍵詞,將初始化變量和賦值,放在一起操作,這種方式簡單,明了。
- := [...]Type{value1, value2, ... , valueN}
array := [...]int{1, 2, 3, 4, 5} // 這種方式,既初始化變量,也是帶了初始值,數組長度,根據初始值的個數而定,也就是五個
多維數組
a := [2][2]int{ {3, 5}, {7, 9}, // This trailing comma is mandatory } fmt.Println(a) // Just like 1D arrays, you don't need to initialize all the elements in a multi-dimensional array. // Un-initialized array elements will be assigned the zero value of the array type. b := [3][4]float64{ {1, 3}, {4.5, -3, 7.4, 2}, {6, 2, 11}, }
二、切片初始化方式
- make ( []Type ,length, capacity )
slice := make([]int, 3, 5)
- make ( []Type, length)
slice := make([]int, 5)
- []Type{}
slice := []int{}
[]string{}
這種方法,初始化成一個大小為0的slice,此時變量(s == nil)已經不成立了,但是s的大小len(s)還是等於0。實際上
[]string{} == make([]string, 0)
。
- []Type{value1 , value2 , ... , valueN }
slice := []int{1, 2, 3, 4, 5} // 這種方式,len是根據初始化長度而定的
為什么什么在初始化slice的時候盡量補全cap
arr := []int{}
arr = append(arr, 1,2,3,4, 5)
arr := make([]int, 0, 5)
arr = append(arr, 1,2,3,4, 5)
在初始化[]int slice的時候在make中設置了cap的長度,就是slice的大小。
這兩種方法對應的功能和輸出結果是沒有任何差別的,但是實際運行的時候,方法2會比少運行了一個growslice的命令。
如果不設置cap,不管是使用make,還是直接使用[]slice 進行初始化,編譯器都會計算初始化所需的空間,使用最小化的cap進行初始化。
a := make([]int, 0) // cap 為0
a := []int{1,2,3} // cap 為3
切片作為函數參數
https://studygolang.com/articles/9876
golang提供了array和slice兩種序列結構。其中array是值類型。slice則是復合類型。slice是基於array實現的。slice的第一個內容為指向數組的指針,然后是其長度和容量。通過array的切片可以切
出slice,也可以使用make創建slice,此時golang會生成一個匿名的數組。
因為slice依賴其底層的array,修改slice本質是修改array,而array又是有大小限制,當超過slice的容量,即數組越界的時候,需要通過動態規划的方式創建一個新的數組塊。把原有的數據復制到新數組,這個新的array則為slice新的底層依賴。
數組還是切片,在函數中傳遞的不是引用,是另外一種值類型,即通過原始變量進行切片
傳入。函數內的操作即對切片的修改操作了。當然,如果為了修改原始變量,可以指定參數的類型為指針類型。傳遞的就是slice的內存地址。函數內的操作都是根據內存地址找到變量本身。
傳引用方式 array和slice作為參數傳遞的過程基本上是一樣的,即傳遞他們切片。有時候我們需要處理傳遞引用的形式。golang提供了指針很方便實現類似的功能。 func main() { slice := []int{0, 1} fmt.Printf("slice %v %p \n", slice, &slice) changeSlice(&slice) fmt.Printf("slice %v %p \n", slice, &slice) slice[1] = -1111 fmt.Printf("slice %v %p \n", slice, &slice) } func changeSlice(s *[]int) { fmt.Printf("func s %v %p \n", *s, s) (*s)[0] = -1 *s = append(*s, 3) (*s)[1] = 1111 } 輸出如下: slice [0 1] 0xc42000a1e0 func s [0 1] 0xc42000a1e0 slice [-1 1111 3] 0xc42000a1e0 slice [-1 -1111 3] 0xc42000a1e0 從輸出可以看到,傳遞給函數的是slice的指針,函數內對對s的操作本質上都是對slice的操作。並且也可以從函數內打出的s地址看到,至始至終就只有一個切片。雖然在append過程中會出現臨時的切片或數組。
三 map初始化
// 1:先聲明map 后make
var m1 map[string]string
// 再使用make函數創建一個非nil的map,nil map不能賦值
m1 = make(map[string]string)
(如果沒有make會報錯:
// 最后給已聲明的map賦值
m1["a"] = "aa"
m1["b"] = "bb"
// 2:直接創建
m2 := make(map[string]string)
// 然后賦值
m2["a"] = "aa"
m2["b"] = "bb"
// 3:初始化 + 賦值一體化
m3 := map[string]string{
"a": "aa",
"b": "bb",
}
// ==========================================
// 查找鍵值是否存在
if v, ok := m1["a"]; ok {
fmt.Println(v)
} else {
fmt.Println("Key Not Found")
}
// 遍歷map
for k, v := range m1 {
fmt.Println(k, v)
}
如下定義:
mapStr := make(map[string]string)
只能儲存string類型的value。
如下定義:
mapInt := make(map[string]int)
只能保存int類型的value。
如下定義:
mapInterface := make(map[string]interface{})
可以保存string、int等不同類型的value。
mu := make([]map[string]interface{},0)
a1 := map[string]interface{} {"id": 1, "parentId": 0, "createTime": "2020-02-02T06:50:36.000+0000", "title": "商品", "level": 0,
"sort": 0, "name": "pms", "icon": "product", "hidden": 0}
a2 := map[string]interface{} {"id": 2, "parentId": 1, "createTime": "2020-02-02T06:51:50.000+0000", "title": "商品列表", "level": 1,
"sort": 0, "name": "product", "icon": "product-list", "hidden": 0}
mu = append(mu, a1)
mu = append(mu, a2)
————————————————
原文鏈接:https://blog.csdn.net/aaronthon/article/details/107777820
map存儲函數:
type Msg func(name string) string
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
wg := &sync.WaitGroup{}
c := make(chan os.Signal, 1)
handleMap := make(map[int]Msg)
handleMap[1] = handle1
handleMap[2] = handle2
handleMap[3] = handle3
golang empty map 和nil map區別
The zero value for a map type is nil, that is, a reference to no hash table at all. 這個nil map是不能添加元素的, 必須進行初始化才可以。 既然如此, 何不默認初始化為empty map呢? 不解!
對C++的NULL進行操作,總是讓人提醒吊膽, 那么在nil map上操作, 會怎樣呢? 繼續看go聖經: Most operations on maps, including lookup, delete, len, and range loops, are safe to perform on a nil map reference, since it behaves like an emtpy map. But storing to a nil map cause a panic.
————————————————
var m map[int]int
if m == nil {
fmt.Println("it is nil, 3")
}else{
fmt.Println("it not not nil, 3")
}
fmt.Println("map len3 is", len(m))
m = make(map[int]int, 0) // 必要,否則panic
if m == nil {
fmt.Println("it is nil, 4")
}else{
fmt.Println("it not not nil, 4")
}
————————————————
參考:鏈接:https://blog.csdn.net/stpeace/article/details/82050482
結構體初始化
方式一:通過 var 聲明結構體
在 Go 語言中當一個變量被聲明的時候,系統會自動初始化它的默認值,比如 int 被初始化為 0,指針為 nil。
var 聲明同樣也會為結構體類型的數據分配內存,所以我們才能像上一段代碼中那樣,在聲明了 var s T
之后就能直接給他的字段進行賦值
var s T s.a = 5 s.b = 8
數組也可以看作是一種結構體類型,不過它使用下標而不是具名的字段
方式二:使用 new
使用 new 函數給一個新的結構體變量分配內存,它返回指向已分配內存的指針:var t *T = new(T)。
type struct1 struct { i1 int f1 float32 str string } func main() { ms := new(struct1) ms.i1 = 10 ms.f1 = 15.5 ms.str= "Chris" fmt.Printf("The int is: %d\n", ms.i1) fmt.Printf("The float is: %f\n", ms.f1) fmt.Printf("The string is: %s\n", ms.str) fmt.Println(ms) }
與面向對象語言相同,使用點操作符可以給字段賦值:structname.fieldname = value
。
同樣的,使用點操作符可以獲取結構體字段的值:structname.fieldname
。
方式三:使用字面量
type Person struct { name string age int address string } func main() { var p1 Person p1 = Person{"lisi", 30, "shanghai"} //方式A p2 := Person{address:"beijing", age:25, name:"wangwu"} //方式B p3 := Person{address:"NewYork"} //方式C }
在(方式A)中,值必須以字段在結構體定義時的順序給出。(方式B)是在值前面加上了字段名和冒號,這種方式下值的順序不必一致,並且某些字段還可以被忽略掉,就想(方式C)那樣。
除了上面這三種方式外,還有一種初始化結構體實體更簡短和常用的方式,如下:
ms := &Person{"name", 20, "bj"} ms2 := &Person{name:"zhangsan"}
&Person{a, b, c}
是一種簡寫,底層仍會調用 new()
,這里值的順序必須按照字段順序來寫,同樣它也可以使用在值前面加上字段名和冒號的寫法(見上文的方式B,C)。
表達式 new(Type)
和 &Type{}
是等價的。
三,幾種初始化方式之間的區別
到目前為止,我們已經了解了三種初始化結構體的方式:
//第一種,在Go語言中,可以直接以 var 的方式聲明結構體即可完成實例化 var t T t.a = 1 t.b = 2 //第二種,使用 new() 實例化 t := new(T) //第三種,使用字面量初始化 t := T{a, b} t := &T{} //等效於 new(T)
使用 var t T
會給 t 分配內存,並零值化內存,但是這個時候的 t 的類型是 T
使用 new 關鍵字時 t := new(T)
,變量 t 則是一個指向 T 的指針
從內存布局上來看,我們就能看出這三種初始化方式的區別:
使用 var 聲明:
使用 new 初始化:
使用結構體字面量初始化:
https://www.cnblogs.com/liyutian/p/10050320.html
一般在進行例如type T struct {a, b int}
的結構體定義之后
習慣使用t := new(T)
給該結構體變量分配內存,它返回指向已分配內存的指針。變量t
是一個指向T
的指針,此時結構體字段的值是它們所屬類型的零值。
聲明 var t T
也會給 t
分配內存,並零值化內存,但是這個時候 t是類型T。在這兩種方式中,t 通常被稱做類型T的一個實例(instance)
或對象(Object)
。
var t *T = new(T)
等價於t := new(T)
。
golang構造函數?
在Go語言中沒有構造函數的概念,對象的創建通常交由一個全局的創建函數來完成,以
NewXXX
來命名,表示“構造函數”:
這一切非常自然,開發者也不需要分析在使用了new之后到底背后發生了多少事情。在Go語言中,一切要發生的事情都直接可以看到。
—— 《Go語言編程》
func NewRect(x, y, width, height float64) *Rect { return &Rect{x, y, width, height} }
slice作為函數參數:
直接貼代碼
func sliceModify(slice []int) { // slice[0] = 88 slice = append(slice, 6) } func main() { slice := []int{1, 2, 3, 4, 5} sliceModify(slice) fmt.Println(slice) }
返回的沒變,坑爹的,這個設計太那啥了,可以正確跑出效果的版本如下:
func sliceModify(slice *[]int) { *slice = append(*slice, 6) } func main() { slice := []int{1, 2, 3, 4, 5} sliceModify(&slice) fmt.Println(slice) }
map作為函數參數:
當你聲明一個map的時候:
m := make(map[int]int)
編譯器會調用 runtime.makemap
:
// makemap implements a Go map creation make(map[k]v, hint) // If the compiler has determined that the map or the first bucket // can be created on the stack, h and/or bucket may be non-nil. // If h != nil, the map can be created directly in h. // If bucket != nil, bucket can be used as the first bucket. func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap
所以實際上是返回一個hmap的指針。
func main(){ m := make(map[int]int) m[1] = 1 fmt.Printf("原始map的內存地址是:%p\n", m) modify(m) fmt.Println("map值被修改了,新值為:", m) } func modify(m interface{}){ fmt.Printf("函數里接收到map的內存地址是:%p\n", p) m := p.(map[int]int) m[1] = 2 }
輸出結果:
-
原始 map的內存地址是:0xc00009e030
-
函數里接收到 map的內存地址是:0xc00009e030
-
map值被修改了,新值為: map[1:2]
在main函數中,m是個指針變量,它保存的值是:0xc00009e030。
在modify函數中,m也是個指針變量,保存的值也是:0xc00009e030。
說明初始化map后,返回的是指針變量,在函數之間,傳遞的是map的地址。
map和channel是相似的。
那么為什么不是 *map[key]value呢,這樣不是更直觀?
Ian Taylor answered this recently in a golang-nuts 原話是:
In the very early days what we call maps now were written as pointers, so you wrote *map[int]int. We moved away from that when we realized that no one ever wrote
map
without writing\*map
.
意思是,一開始也是寫作 *map[key]value,后來發現所有的map都是當作指針來用的,於是就省略簡寫了。
轉載於:https://www.cnblogs.com/zhouj-happy/p/10962500.html
golang學習筆記 ---解析(map[string]interface{})數據格式
interface轉其他類型
有時候返回值是interface類型的,直接賦值是無法轉化的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package
main
import
(
"fmt"
)
func
main() {
var
a
interface
{}
var
b string
a =
"123"
//b = a //cannot use a (type interface {}) as type string in assignment: need type assertion
b = a.(string)
fmt.Println(b)
}
|
輸出:
123
通過a.(string) 轉化為string,通過a.(int)轉化為類型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
main
import
(
"fmt"
)
func
main() {
var
a
interface
{}
var
b string
var
c int
a =
"123"
//b = a //cannot use a (type interface {}) as type string in assignment: need type assertion
b = a.(string)
fmt.Println(b)
a = 124
c = a.(int)
fmt.Println(c)
}
|
可以通過a.(type)來判斷a可以轉為什么類型。
注意事項
- map記得分配內存
- 解析出來的int類型會變成float64類型
- 注意判斷不為nil后再轉換類型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package
main
import
(
"encoding/json"
"fmt"
)
func
main() {
//1.准備一段json
b := []byte(`{
"Name"
:
"sary"
,
"Age"
:18}`)
//2.聲明空接口
var
i
interface
{}
//3.解析
err := json.Unmarshal(b, &i)
if
err != nil {
fmt.Println(
"json err:"
, err)
}
//自動轉map
fmt.Println(i)
//4.使用interface的json,可以判斷類型
m := i.(
map
[string]
interface
{})
for
k, v :=
range
m {
switch
value := v.(
type
) {
case
nil:
fmt.Println(k,
"is nil"
,
"null"
)
case
string:
fmt.Println(k,
"is string"
, value)
case
int:
fmt.Println(k,
"is int"
, value)
case
float64:
fmt.Println(k,
"is float64"
, value)
case
[]
interface
{}:
fmt.Println(k,
"is an array:"
)
for
i, u :=
range
value {
fmt.Println(i, u)
}
case
map
[string]
interface
{}:
fmt.Println(k,
"is an map:"
)
default
:
fmt.Println(k,
"is unknown type"
, fmt.Sprintf(
"%T"
, v))
}
}
}
|
輸出:
map[Age:18 Name:sary]
Name is string sary
Age is float64 18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
package
main
import
(
"encoding/json"
"fmt"
)
func
main() {
var
m
map
[string]
interface
{}
//聲明變量,不分配內存
m = make(
map
[string]
interface
{})
//必可不少,分配內存
m[
"name"
] =
"sary"
var
age int = 18
m[
"age"
] = age
m[
"addr"
] =
"China"
print_map(m)
fmt.Println()
data, err := json.Marshal(m)
if
err != nil {
fmt.Println(
"err:"
, err)
}
else
{
fmt.Println(
"data:"
, data)
}
m1 := make(
map
[string]
interface
{})
err = json.Unmarshal(data, &m1)
if
err != nil {
fmt.Println(
"err:"
, err)
}
else
{
print_map(m1)
}
fmt.Println()
value, ok := m1[
"key1"
]
if
ok {
fmt.Printf(value.(string))
}
else
{
fmt.Println(
"key1 不存在"
)
}
}
//解析 map[string]interface{} 數據格式
func
print_map(m
map
[string]
interface
{}) {
fmt.Println(
"enter print_map########### "
)
for
k, v :=
range
m {
switch
value := v.(
type
) {
case
nil:
fmt.Println(k,
"is nil"
,
"null"
)
case
string:
fmt.Println(k,
"is string"
, value)
case
int:
fmt.Println(k,
"is int"
, value)
case
float64:
fmt.Println(k,
"is float64"
, value)
case
[]
interface
{}:
fmt.Println(k,
"is an array:"
)
for
i, u :=
range
value {
fmt.Println(i, u)
}
case
map
[string]
interface
{}:
fmt.Println(k,
"is an map:"
)
print_map(value)
default
:
fmt.Println(k,
"is unknown type"
, fmt.Sprintf(
"%T"
, v))
}
}
fmt.Println(
"out print_map ########### "
)
}
|
輸出:
enter print_map###########
name is string sary
age is int 18
addr is string China
out print_map ###########
data: [123 34 97 100 100 114 34 58 34 67 104 105 110 97 34 44 34 97 103 101 34 58 49 56 44 34 110 97 109 101 34 58 34 115 97 114 121 34 125]
enter print_map###########
addr is string China
age is float64 18
name is string sary
out print_map ###########
key1 不存在
golang中結構體的初始化方法(new方法)
自定義一個結構體
type Rect struct {
x, y float64
width, height float64
}
初始化方法:
rect1 := new(Rect)
rect2 := &Rect{}
rect3 := &Rect{0, 0, 100, 200}
rect4 := &Rect{width:100, height:200}
注意這幾個變量全部為指向Rect結構的指針(指針變量),因為使用了new()函數和&操作符.而如果使用方法
a := Rect{}
則表示這個是一個Rect{}類型.兩者是不一樣的.參考代碼:
func main() {
rect1 := &Rect{0, 0, 100, 200}
rect1.x = 10
a := Rect{}
a.x = 15
fmt.Printf("%v\n%T\n", a, a)
fmt.Printf("%v\n%T\n", rect1, rect1)
}
運行結果為:
{15 0 0 0}
main.Rect
&{10 0 100 200}
*main.Rect
從結果中可以清楚的看到兩者的不同.
在Go語言中,未進行初始化的變量都會被初始化為該類型的零值,例如bool類型的零值為false, int類型的零值為0, string類型的零值為空字符串. 在Go語言中沒有構造函數的概念,對象的創建通常交由一個全局的創建函數來完成,以NewXXX來命令,表示"構造函數":
func NewRect(x ,y ,width, height float64) {
return &Rect{x, y, width, height}
}
這一切非常自然.開發者也不需要分析在使用了new之后到底背后發生了多少事情.在Go語言中,一切要發生的事情都直接可以看到. 附:
用 new 分配內存 內建函數 new 本質上說跟其他語言中的同名函數功能一樣:new(T) 分配了零值填充的 T 類型的內存空間,並且返回其地址,一個 *T 類型的值。用 Go 的術語說,它返回了一個指針,指向新分配的類型 T 的零值。記住這點非常重要。 這意味着使用者可以用 new 創建一個數據結構的實例並且可以直接工作。如 bytes.Buffer的文檔所述 “Buffer 的零值是一個准備好了的空緩沖。” 類似的,sync.Mutex 也沒有明確的構造函數或 Init 方法。取而代之,sync.Mutex 的零值被定義為非鎖定的互斥量。 零值是非常有用的。例如這樣的類型定義,56 頁的”定義自己的類型” 內容。 ===================
務必記得 make 僅適用於 map,slice 和 channel,並且返回的不是指針。應當用 new獲得特定的指針。
map判斷key是否存在
if _, ok := map[key]; ok { // 存在 } if _, ok := map[key]; !ok { // 不存在 }
// Go 語言中 range 關鍵字用於for循環中迭代數組(array)、切片(slice)、 // 鏈表(channel)或集合(map)的元素。在數組和切片中它返回元素的索引值, // 在集合中返回 key-value 對的 key 值。 //range是取出兩值 nums := []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) //在數組上使用range將傳入index和值兩個變量。 // 上面那個例子我們不需要使用該元素的序號,所以我們使用空白符"_"省略了。 // 有時侯我們確實需要知道它的索引。 for i1, num1 := range nums { if num1 == 3 { fmt.Println("index:", i1) //索引從0開始 } } //range也可以用在map的鍵值對上。 kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } //range也可以用來枚舉Unicode字符串。第一個參數是字符的索引,第二個是字符(Unicode的值)本身。 for i2, c := range "go" { fmt.Println(i2, c)//得出的c是ASCII值 }
map和切片的組合定義
元素為map的slice
var userInfo = make([]map[string]string, 2, 2) if userInfo[0] == nil { //map 的默認值是nil userInfo[0] = make(map[string]string) userInfo[0]["name"] = "lili" userInfo[0]["age"] = "20" userInfo[0]["sex"] = "女" } if userInfo[1] == nil{ userInfo[1] = make(map[string]string) userInfo[1]["name"] = "liwang" userInfo[1]["age"] = "30" userInfo[1]["sex"] = "男" }
元素為切片的map
var studentData = make(map[string][]string) studentData["age"] = []string{ "12", "13", "15", } studentData["name"] = []string{ "xiaomi", "xiaoli", "xiaowang", } studentData["sex"] = []string{ "女", "女", "男", } fmt.Println(studentData) //map[age:[12 13 15] name:[xiaomi xiaoli xiaowang] sex:[女 女 男]]
小應用:統計單詞出現的次數:
var str = "hello world hello world do do do" var strSlice = strings.Split(str," ") var mapStr = make(map[string]int) for _, v := range strSlice { mapStr[v]++ } fmt.Println(mapStr) // map[do:3 hello:2 world:2]