Golang數據類型 數組(arry)


前言

前面學習了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
"""
Python的list可變+引用

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
*/
Golang的arry可變+拷貝

 

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
'''
Python的list切片

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)
}
Golang的Arry切片

 

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
}
Arry內存管理機制

 

 

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("用戶和密碼都沒有輸入!")

    }
}
Doing more practice

 

 

see also

 


免責聲明!

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



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