溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

golang 數(shù)據(jù)一   (字符串、數(shù)組和數(shù)組指針)

發(fā)布時(shí)間:2020-07-05 16:56:01 來源:網(wǎng)絡(luò) 閱讀:12670 作者:100018 欄目:編程語言

從如下幾個(gè)方面介紹GO語言的數(shù)據(jù)

1. 字符串
2. 數(shù)組
3. 切片
4. 字典
5. 結(jié)構(gòu)
字符串

Go語言中的字符串是由一組不可變的字節(jié)(byte)序列組成從源碼文件中看出其本身是一個(gè)復(fù)合結(jié)構(gòu)

string.go 
type stringStruct struct {
    str unsafe.Pointer    
    len int
}

字符串中的每個(gè)字節(jié)都是以UTF-8編碼存儲的Unicode字符字符串的頭部指針指向字節(jié)數(shù)組的開始但是沒有NULL或'\0'結(jié)尾標(biāo)志。 表示方式很簡單用雙引號("")或者反引號(``)它們的區(qū)別是

  1. 雙引號之間的轉(zhuǎn)義符會被轉(zhuǎn)義而反引號之間的轉(zhuǎn)義符保持不變

  2. 反引號支持跨行編寫而雙引號則不可以


{
    println("hello\tgo")    //輸出hello    go
    println(`hello\tgo`)    //輸出hello\tgo
}

{
    println( "hello 
    go" )//syntax error: unexpected semicolon or newline, expecting comma or )

    println(`hello                                                                                                                                                                                                 
        go`) //可以編譯通過
}
輸出
hello 
    go

在前面類型的章節(jié)中描述過字符串的默認(rèn)值是""而不是nil,比如

var s string
println( s == "" )  //true
println( s == nil ) //invalid operation: s == nil (mismatched types string and nil)

Go字符串支持 "+ , += , == , != , < , >" 六種運(yùn)算符

Go字符串允許用索引號訪問字節(jié)數(shù)組(非字符)但不能獲取元素的地址比如

{    
    var a = "hello"
    println(a[0])       //輸出 104
    println(&a[1])      //cannot take the address of a[1]
}

Go字符串允許用切片的語法返回子串(起始和結(jié)束索引號)比如

var a = "0123456"                                                                                                                                                                                              
println(a[:3])      //0,1,2
println(a[1:3])     //1,2
println(a[3:])      //3,4,5,6

日常開發(fā)中經(jīng)常會有遍歷字符串的場景比如

{
    var a = "Go語言"
    for i:=0;i < len(a);i++{                //以byte方式按字節(jié)遍歷
        fmt.Printf("%d: [%c]\n", i, a[i])
    }
    for i, v := range a{                    //以rune方式遍歷                                                                                               
        fmt.Printf("%d: [%c]\n", i, v)
    }
}
輸出    
0: [G]
1: [o]
2: [è]
3: [ˉ]
4: [-]
5: [è]
6: [¨]
7: []
0: [G]
1: [o]
2: [語]
5: [言]

在Go語言中字符串的底層使用的是不可以改變的byte數(shù)組存的所以在用byte輪詢方式時(shí)每次得到的只有一個(gè)byte而中文字符則是占3個(gè)byte的。rune采用計(jì)算字符串長度的方式與byte方式不同比如

println(utf8.RuneCountInString(a)) // 結(jié)果為  4
println(len(a)) // 結(jié)果為 8

所以如果想要獲得期待的那種結(jié)果的話需要先將字符串a(chǎn)轉(zhuǎn)換為rune切片再使用內(nèi)置的len函數(shù)比如:

{
    r := []rune(a)
    for i:= 0;i < len(r);i++{
        fmt.Printf("%d: [%c]\n", i, r[i])
    }
}

所以在遍歷或處理的字符串的情況下如果其中存在中文盡量使用rune方式處理。

轉(zhuǎn)換

前面講過不能修改原字符串如果修改的話需要將字符串轉(zhuǎn)換成[]byte或[]rune , 然后在轉(zhuǎn)換回來比如

{
    var a = "hello go"                                                                                                       
    a[1] = 'd'              //cannot assign to a[1]
}
{
    var a = "hello go"
    bs := []byte(a)
    ...                                                                                                                                                                                                            
    s2 := string(bs)

    rs := []rune(a)
    ...
    s3 := string(rs)
}

Go語言支持用"+"運(yùn)算符進(jìn)行字符串拼接但是每次拼接都需要重新分配內(nèi)存如果頻繁構(gòu)造一個(gè)很長的字符串則性能影響就會很大比如

func test1()string{
    var s string
    for i:= 0;i < 1000 ;i++{
        s += "a" 
    }   
    return s
}

func Benchmark_test1(b *testing.B){
    for i:= 0;i < b.N; i++{
        test1()                                                                                                                                                                                                    
    }   
}
輸出
# go test str1_b_test.go  -bench="test1" -benchmem
Benchmark_test1-2   	    5000	    227539 ns/op	  530338 B/op	     999 allocs/op

常用的改進(jìn)方法是預(yù)分配足夠的內(nèi)存空間然后使用strings.Join函數(shù)該函數(shù)會統(tǒng)計(jì)出所有參數(shù)的長度并一次性完成內(nèi)存分配操作改進(jìn)一下上面的代碼

func test()string{
    s := make([]string,1000)
    for i:= 0;i < 1000 ;i++{
        s[i] = "a" 
    }   
    return strings.Join(s,"")
}
func Benchmark_test(b *testing.B){
    for i:= 0;i < b.N; i++{
        test()
    }   
}
輸出
# go test -v b_test.go  -bench="test1" -benchmem
Benchmark_test1-2   	  200000	     10765 ns/op	    2048 B/op	       2 allocs/op

在日常開發(fā)中可以使用fmt.Sprintf函數(shù)來格式化和拼接較少的字符串操作比如

{
    a := 10010
    as := fmt.Sprintf("%d",a)
    fmt.Printf("%T , %v\n",as,as)
}

數(shù)組

數(shù)組是內(nèi)置(build-in)類型是一組存放相同類型數(shù)據(jù)的集合數(shù)組的數(shù)據(jù)類型是由存儲的元素類型和數(shù)組的長度共同決定的,即使元素類型相同但是長度不同數(shù)組也不屬于同一類型。數(shù)組初始化之后長度是固定無法修改的數(shù)組也支持邏輯判斷運(yùn)算符 "==","="定義方式如下

{
    var a [10]int
    var b [20]int
    println(a == b)  //invalid operation: a == b (mismatched types [10]int and [20]int)
}

數(shù)組的初始化相對靈活下標(biāo)索引值從0開始支持按索引位置初始化對于未初始化的數(shù)組編譯器將給以默認(rèn)值。

{
    var a[4] int                //元素初始化為0
    b := [4] int{0,1}           //未初始化的元素將被初始化為0
    c := [4] int{0, 2: 3}       //可指定索引位置初始化
    d := [...]int{0,1,2}        //編譯器根據(jù)初始化值數(shù)量來確定數(shù)組的長度
    e := [...]int{1, 3:3}       //支持索引位置初始化但數(shù)組長度與其無關(guān)
    
    type user struct{
        name string
        age int 
    }   

    d := [...] user{            //復(fù)合數(shù)據(jù)類型數(shù)組可省略元素初始化類型標(biāo)簽
        {"a",1},
        {"b",2},
    }
}

定義多維數(shù)組時(shí)只有數(shù)組的第一維度允許使用 "..."

{
    x := [2]int{2,2}
    a := [2][2]int{{1,2},{2,2}}
    b := [...][2]int{{2,3},{2,2},{3,3}}
    c := [...][2][2]int{{ {2,3},{2,2} },{{3,3},{4,4}} }
}

計(jì)算數(shù)組長度時(shí)無論使用內(nèi)置的len還是cap返回的都是第一維度的長度比如

{
    fmt.Println(x, len(x), cap(x))    
    fmt.Println(a, len(a), cap(x))
    fmt.Println(b, len(b), cap(x))
    fmt.Println(c, len(c), cap(x))
}
輸出
[2 2] 2 2
[[1 2] [2 2]] 2 2
[[2 3] [2 2] [3 3]] 3 2
[[[2 3] [2 2]] [[3 3] [4 4]]] 2 2

數(shù)組指針&指針數(shù)組

數(shù)組除了可以存放具體類型的數(shù)據(jù)也可以存放指針比如

{ 
    x, y := 10, 20
    a := [...]*int{&x, &y}      //指針數(shù)組 
    p := &a                     //數(shù)組的指針
}

數(shù)組復(fù)制

Go語言數(shù)組是值(非引用)類型所以在賦值和參數(shù)傳遞過程中都會復(fù)制整個(gè)數(shù)組數(shù)據(jù)比如:

func test(x [2]int){
    fmt.Printf("x:= %p,%v\n", &x, x)
}

func main(){ 
    a := [2] int{1, 2}
    test(a)                     //傳參過程中完全復(fù)制
    var b [2]int
    b = a                       //賦值過程中完全復(fù)制
    fmt.Printf("a:= %p,%v\n", &a, a)
    fmt.Printf("b:= %p,%v\n", &b, b)                                                                                                                                                                               
}
輸出
x:= 0xc42000a330,[1 2]
a:= 0xc42000a320,[1 2]
b:= 0xc42000a370,[1 2]


借鑒: 雨痕<GO學(xué)習(xí)筆記>

討論學(xué)習(xí): 675020908

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI