前言
前面學習了Go數據類型中的數字、字符串,下面看看Go中是否有Python中的list、dict等復合數據類型?
需要注意的是在Go中數組是可變類型+值(拷貝)類型,而go中切片是引用類型;
數組聲明之后它里面的元素是可以被修改,如果把1個數組變量引用到1個新的變量,那么這個新的變量會重新拷貝一份舊數組的數組。
Python中的list是可變+引用數據類型
llist1 = ["A", "B", "C"] llist2 = llist1 llist1[0] = "a" llist2[2] = "b" print(llist1, id(llist1)) print(llist2, id(llist2)) """ ['a', 'B', 'b'] 42775624 ['a', 'B', 'b'] 42775624 """
Golang中的arry是可變+拷貝數據類型
package main import "fmt" func main() { arry1 := [3]string{"A", "B", "C"} //注意聲明arry變量時不要忘記[指定長度],否則聲明的是slice變量 arry2 := arry1 arry1[0] = "a" arry2[1] = "b" //說明go中的arry不是引用類型,單屬於可變數控類型可以原處修改 fmt.Printf("%v %p\n", arry1, &arry1) fmt.Printf("%v %p\n", arry2, &arry2) } /* [a B C] 0xc000062330 [A b C] 0xc000062360 */
Python的list切片之后會開辟1開新的內存生產1個新的list數據類型
nameList=["張","根"] firstName=nameList[0] lastName=nameList[1] print(firstName) print(lastName) #Python的list切片會開辟1塊新的內存,創建1個新的列表。 nameSlice=nameList[0:2] nameSlice[0]="Martin" nameSlice[1]="Zhang" print(nameList,nameSlice) print(type(nameList),type(nameSlice)) print(id(nameList),id(nameSlice)) ''' 張 根 ['張', '根'] ['Martin', 'Zhang'] <class 'list'> <class 'list'> 42632648 42632712 '''
Golang的arry切片之后會開辟1個新的內存生成1個slice數據類型
package main import ( "fmt" "reflect" ) func main() { nameArry:=[2]string{"張","根"} //1.獲取數組的長度 fmt.Println(len(nameArry)) //2.通過數組索引取值 firstName:=nameArry[0] lastName:=nameArry[1] fmt.Println(firstName) fmt.Println(lastName) //3.數組組切片 nameSlice:=nameArry[0:2] nameSlice[0]="Martin" nameSlice[1]="Zhange" fmt.Println(nameArry,nameSlice) //[Martin Zhange] [Martin Zhange] fmt.Println(reflect.TypeOf(nameArry),reflect.TypeOf(nameSlice)) //[2]string(數組類型)-------- []string(切片類型) fmt.Printf("切片之前的內存地址:%p,切片之后的slice內存地址:%p-- ",&nameArry,nameSlice) }
package main
import "fmt"
func main() {
//聲明1個字符串變量
var name string
fmt.Println(name)
//聲明1個數組變量
var a1 [3]int
fmt.Println(a1)
//聲明1個切片類型的變量
var s1 []int
fmt.Println(s1 == nil)
//聲明二維數組
var a3 [3][3]int
//對二維數組進行初始化
a3 = [3][3]int{
[3]int{1, 2, 3},
[3]int{4, 5, 6},
[3]int{7, 8, 8},
}
fmt.Println(a3)
//
a4 := [3]int{1, 2, 3}
a5 := modifyArry(a4)
fmt.Println(a4)
fmt.Println(a5)
s2 := []string{"河北", "保定", "唐縣"}
s3 := modifySlice(s2)
fmt.Println(s2)
fmt.Println(s3)
}
//數組是值類型:修改數組時,會復制 1個新的數組讓我們修改
func modifyArry(a1 [3]int) (ret [3]int) {
a1[0] = 100
return a1
}
//切片是引用類型:修改切片時,不會復制1個新的切片讓我們修改(修改原來的)
func modifySlice(s2 []string) (ret2 []string) {
s2[0] = "河北省"
return s2
}
數組
數組就是存放同1種數據基本數據類型(數值,字符、字符串、布爾值)元素的容器。
Go中的數組有一下特點:
在聲明arry變量時就要聲明arry的長度、容器內存放元素的數據類型;
數組的長度是數據類型的1部分,所以2個長度不同arrr,即使他們都屬於數組,但2者的數據類型完全不一致,所以2者無法相互比較和賦值。
數組如果沒有初始化里面的元素默認為0值(bool=false,int or float=0, string=空字符串)
數組的聲明和初始化
package main
import "fmt"
func main() {
//數組:存放同1中基礎數據類型的容器
//聲明1個長度為3的數組,里面存放bool數據類型
var arry1 [3]bool
var arry2 [4]bool
var arry3 [3]bool
fmt.Printf("arry1:%T arry2:%T arry3:%T\n", arry1, arry2, arry3)
fmt.Println(arry1 == arry3)
//初始化方式1:
arry1 = [3]bool{true, true, true}
fmt.Println(arry1) //[true true true]
//初始化方式2:[...]自動推算數組的長度
arry4 := [...]int{1, 2, 3, 4}
fmt.Println(arry4)
//初始化方式3:利用默認值
arry5 := [5]int{12, 13}
fmt.Println(arry5) //[12 13 0 0 0]
//初始化方式4:根據索引指定arry中的元素
arry6 := [5]int{0: 1, 4: 6}
fmt.Println(arry6)//[1 0 0 0 6]
}
遍歷數組
package main import ( "fmt" "reflect" ) func main() { nameArry := [2]string{"張", "根"} //1.獲取數組的長度 fmt.Println(len(nameArry)) //2.通過數組索引取值 firstName := nameArry[0] lastName := nameArry[1] fmt.Println(firstName) fmt.Println(lastName) //3.數組組切片 nameSlice := nameArry[0:2] nameSlice[0] = "Martin" nameSlice[1] = "Zhange" fmt.Println(nameArry, nameSlice) //[Martin Zhange] [Martin Zhange] fmt.Println(reflect.TypeOf(nameArry), reflect.TypeOf(nameSlice)) //[2]string(數組類型)-------- []string(切片類型) fmt.Printf("切片之前的內存地址:%p,切片之后的slice內存地址:%p--\n", &nameArry, nameSlice) //2.循環數組 fmt.Println(nameArry) for i := 0; i < len(nameArry); i++ { fmt.Println(i, nameArry[i]) } for item := range nameArry { fmt.Println(item) } for index := range nameArry { fmt.Println(index) } for index, item := range nameArry { fmt.Println(index, item) } }
多維數組
多維數組就是數組里面嵌套數組
package main
import (
"fmt"
)
func main() {
//定義多維數組
a1 := [3][2]int{{1, 2}, {1, 3}, {1, 4}}
fmt.Println(a1) //[[1 2] [1 3] [1 4]]
//遍歷多維數組
for _, v := range a1 {
for _, v1 := range v {
fmt.Println(v1)
}
}
}
數組的內存管理機制

Golang字符串類型存儲的機制

我們在Golang中聲明的字符串類型變量,編譯器不會直接存儲該字符串的值。
而是先開辟1塊內存把字符串的值存儲起來,然后開辟1塊內存把這個字符串的指針(8個字節)和長度(8個字節)存儲起來賦予變量。
為什么要這樣存儲1個字符串呢?額外開辟1塊存儲指針和長度的內存空間。
我感覺這是是為了字符串被放到到Arry或Slice之后的查詢效率而做的鋪墊。
1.數組的內存地址是連續的
2.數組的內存地址=數組中第1個元素的內存地址
這種設計可以讓我們快速的從1個元素獲取到數組中最后1個元素,查詢效率大大提升。
既然聲明1個數組即開辟了1塊連續的內存,對Arry中的item進行順序存儲,那么Arry的item含有字符串呢?
字符串可能是英文/漢字它們占用內存的規格可不一致啊!既然如此那我就同一一下規格!
3.聲明1個字符串數組時,開啟一段連續的內存之后,(並不會在這段連續的內存上直接存儲字符串的值,而是存儲字符串值的指針(占4個字節)和長度(占4字節)=16個字節。這也是Golang中字符串數據類型的存儲機制,連續的內存+指針=加快查詢效率...)
這樣Arry中每1個元素對這段連續內存的使用都有了固定的規格,節約了Arry對內存空間占用。
隨意字符串存儲時增加了額外的空間去存儲指針和長度,但是加速了查詢效率。用空間換時間吧!
package main import "fmt" //1.數組的內存是連續的 //2.因為數組的內存是連續的,所以數組的內存地址就是數組里第1個元素的內存地址。 func main() { numbers := [3]int8{11, 22, 33} fmt.Printf("%p\n", &numbers) //0xc00000a0a8 fmt.Println(numbers) //[11 22 33] fmt.Printf("%p\n", &numbers[0]) //0xc00000a0a8 fmt.Printf("%p\n", &numbers[1]) //0xc00000a0a9 fmt.Printf("%p\n", &numbers[2]) //0xc00000a0aa numbers32 := [3]int32{11, 22, 33} fmt.Printf("%p\n", &numbers32) //0xc00011e0b4 fmt.Println(numbers) //[11 22 33] fmt.Printf("%p\n", &numbers32[0]) //0xc00011e0b4 fmt.Printf("%p\n", &numbers32[1]) //0xc00011e0b8 fmt.Printf("%p\n", &numbers32[2]) //0xc00011e0bc var names = [2]string{"張根", "Martin"} fmt.Printf("%p\n", &names) //0xc0000044c0 fmt.Println(names) //[張根 Martin] fmt.Printf("%p\n", &names[0]) //0xc000114460 fmt.Printf("%p\n", &names[1]) //0xc000114470 }
Go中多維數組是數值類型不是引用類型
值類型:就是重新開辟新的內存,與引用類型相反
package main
import (
"fmt"
)
func main() {
//arry數值類型辯證
a1 := [...]string{"河北", "河南", "山東"}
a2 := a1
a1[len(a1)-1] = "山西"
fmt.Println(a1) //[河北 河南 山西]
fmt.Println(a2) //[河北 河南 山東]
}
練習題
package main
import (
"fmt"
)
func main() {
//1.求數組[1, 3, 5, 7, 8]所有元素的和
arr1 := [...]int{1, 3, 5, 7, 8}
var sum int
for _, v := range arr1 {
sum += v
}
fmt.Println(sum)
/*找出數組中和為指定值的兩個元素的下標,比如從數組[1, 3, 5, 7, 8]中找出和為8的兩個元素的下標分別為(0,3)和(1,2)*/
for i := 0; i < len(arr1); i++ {
for j := i + 1; j < len(arr1); j++ {
if arr1[i]+arr1[j] == 8 {
fmt.Printf("%d %d\n", i, j)
}
}
}
}
package main import "fmt" /* 1.Go語言的int占多少個字節? 答:取決於你的操作系統 32位 4個字節,64位8個字節 2.整型中有符號和無符號是什么意思? 答:所謂有符號是使用1個位表示征服性質,因為使用第1位表示正負性質,所以有符號整型可表示的正數范圍比較小,無符號整型表示的整數范圍比較大! 3.整型可以表示的最大范圍是 答:0-2**64,超出之后使用bigint包 4.什么是nil 答:nil就是空的數據 5.十進制是以整型的方式存在,其他其他進制以字符串的形式存在?如何實現進制之間的轉換? 答:在golang中10進制是以整型存在,其他進制(二進制、八進制、十六進制)都是以字符串的形式存在 bynaryNumber:=strconv.FormatInt(int64(29),2)//十進制轉二進制 octalNumber:=strconv.FormatInt(int64(29),8)//十進制轉八進制 hexaDecimal:=strconv.FormatInt(int64(29),16)//十進制轉八進制 fmt.Println(bynaryNumber,octalNumber,hexaDecimal) fmt.Println(strconv.ParseInt(hexaDecimal,16,32)) 6.簡述如下代碼的意義:var v1 int|var v2 *int var v3=new(int) var v1 int //開辟1塊個內存+內存初始化值位0,v1指向該內存地址,打印顯示0 fmt.Println(v1) v2:=999 //開辟1塊個內存+內存初始化值設置為999,v2指向該內存地址,打印顯示0 fmt.Println(v2) var v3 *int fmt.Printf("編譯器開辟1塊名為:%p的內存 存儲了1個%p的空指針,空指針指向了nil(並沒有初始化)\n",&v3,v3) v4:=new(int) fmt.Printf("編譯器開辟1塊名為:%p的內存 存儲了1個指針%p,指針指向%p內存這塊內存上存了值%d\n",&v4,v4,&*v4,*v4) 7.浮點型為什么無法精准表示小數? 答:這取決於 浮點型的小數部。小數部分 分轉換成二進制時得不出1就這個浮點型就無法精確表達。 8.如何使用第3方包decimal? 答: var v1 =decimal.NewFromFloat(0.0000019) var v2 =decimal.NewFromFloat(0.298766) var v3 =v1.Add(v2) var v4 =v3.Sub(v2) var v5 =v4.Mul(v2) var v6 =v4.Div(v1) fmt.Println(v3) fmt.Println(v4) fmt.Println(v5) fmt.Println(v6) var price=decimal.NewFromFloat(3.6615926) fmt.Println(price.Round(1)) //保留小數點后1位自動四舍五入 fmt.Println(price.Truncate(2))//保留小數點后2位不需要四舍五入 9.簡述ascii、Unicode、utf-8的關系 答: American Standard Code for Information Interchange 是存儲了歐美國家在計算機中可以用到字符和對應二進制的字符集,這些二進制使用8位表示足夠 Unicode:描述全球人使用字符和對應碼位的字符集 使用32位表示碼位范圍。 utf-8是對unicode的碼位進行畫區間然后根據字符的碼位區間進行模板選擇完成Unicode碼位和模板的填充 10.判斷Go語言中的字符是utf-8編碼的字節 name:="張根" fmt.Println(strconv.FormatInt(int64(name[0]),2)) fmt.Println(strconv.FormatInt(int64(name[1]),2)) fmt.Println(strconv.FormatInt(int64(name[2]),2)) //11100101 10111100 10100000 去對應utf-8的模板一看便知 11.什么是rune? rune表示英文字符或者漢字在Unicode字符中對應的碼位 name:="張根" EnglishName:="Martin" fmt.Println([]rune(name)) fmt.Println([]rune(EnglishName)) [24352 26681] [77 97 114 116 105 110] 12.判斷:字符串是否可變? 答:不可變 13.列舉你記得的字符串常見功能 答:Prefix() stufix() 14.字符串集合和rune集合如何實現相互轉換 name:="Alex" newRune:=[]rune(name) newRune[0]='a' fmt.Println(string(newRune)) 15.如何實現字符串高效率拼接 var builder strings.Builder builder.WriteString("我愛") builder.WriteString("北") builder.WriteString("京") result11:=builder.String() fmt.Println(result11) 16.簡述數組的存儲原理 答:一塊連續的內存地址,第一個元素即該數組的內存地址。如果數組內包含字符串 存儲字符串(長度+指針)進行統一規格! 17.names:=[3]string{"alex","超級無敵小JJ","傻兒子"} a.根據索引獲取“傻兒子” b.根據索引獲取“alex” c.根據索引獲取“小JJ” d.請將names數組最后1個元素修改為“大燒瓶” 答: names := [3]string{"alex", "超級無敵小JJ", "傻兒子"} fmt.Println(names[0]) fmt.Println(names[1]) fmt.Println(names[2]) names[0]="大燒餅" fmt.Println(names) 18.看代碼輸出結果 var nestdata [3][2]int fmt.Println(nestdata) [[0 0] [0 0] [0 0]] 19.請聲明1個3個元素的數組,元素的類型是數組,並在數組中初始化值 var nestdata=[3][2]int{{1,2},{3,4},{5,6}} fmt.Println(nestdata) 20循環下列數組並使用字符串格式化輸出一下內容: dataList:=[ ["alex","qwe123"], ["eric","qw2"], ["tony","qpep23"], ] 最終實現: 我是Alex我的密碼是qwe123 答: var userList = [3][2]string{{"alex", "qwe123"}, {"eric", "qw2"}, {"tony", "qpep23"}} fmt.Println(userList) for _, item := range userList { fmt.Printf("我是%s我的秘密是%s\n", item[0], item[1]) } 21.實現用戶登錄 //userList中有3個用戶, dataList:=[ ["alex","qwe123"], ["eric","qw2"], ["tony","qpep23"], ] 從dataList中獲取用戶名驗證用戶合法性 */ func main() { var userList = [3][2]string{{"alex", "qwe123"}, {"eric", "qw2"}, {"tony", "qpep23"}} var userName string var passWord string fmt.Print("請輸入用戶名: ") fmt.Scan(&userName) fmt.Print("請輸入秘密: ") fmt.Scan(&passWord) isAuthenticated := 0 if len(userName) > 0 && len(passWord) > 0 { isAuthenticated = 197 for _, item := range userList { if userName == item[0] { //當用戶名和密碼全部正確是也會走這個if分支! isAuthenticated = 198 } if passWord == item[1] { //當用戶名和密碼全部正確是也會走 這個if分支! isAuthenticated = 199 } if userName == item[0] && passWord == item[1] { isAuthenticated = 200 break } } } switch isAuthenticated { case 200: fmt.Println("登錄成功!") case 197: fmt.Println("用戶和密碼都不對!") case 198: fmt.Println("用戶對了,密碼不對!") case 199: fmt.Println("密碼對了,用戶名不對!") default: fmt.Println("用戶和密碼都沒有輸入!") } }
