go_切片

拆尼斯、帕丁顿發表於2024-04-07

切片

# 定義
切片是由陣列建立的一種方便、靈活且功能強大的包裝(Wrapper)。切片本身不擁有任何資料。它們只是對現有陣列的引用
陣列定長,長度不能改變---》python中列表----》go切片


# 知識點
1 建立一個切片(透過陣列建立)
    
2 使用 make 建立一個切片    
3 切片的修改
4 切片的長度和容量

5 追加切片元素
6 切片的函式傳遞
7 多維切片
8 記憶體最佳化
——————————————————————————————————
package main

import "fmt"

// 切片

func main() {
    // 1 建立一個切片(透過陣列建立)
    var a [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    // 切片--->從a陣列上,從0到結尾,切片賦值給s
    //var s = a[:]
    var s []int = a[:]
    fmt.Println(s)
    // 檢視型別--->中括號中沒有數字-->就是切片,有數字就是陣列
    // 區分陣列(值)和切片(引用)型別
    fmt.Printf("s的型別是:%T\n", s) // []int

    // 2 使用 make 建立一個切片-->必須透過make或透過陣列切出來,否則是空的
    var s1 []string = make([]string, 4)
    fmt.Println(s1)

    // 3 切片零值
    // 3.1 透過陣列切出來,不會為 空
    // 3.2 如果只定義,沒有初始化--》零值---》空---》nil
    var s2 []string
    fmt.Println(s2) // []

    // 4 操作切片---》取值,賦值---》使用 [數字]
    s1[0] = "lqz"
    s1[1] = "彭于晏"
    fmt.Println(s1)
    s1[0] = "ssdfasdf"
    fmt.Println(s1)

    // 5 如果切片為  空,nil--》沒有初始化 不能使用(取值賦值)

    s2 = make([]string, 1) // 不是nil了,但是長度為0
    if s2 == nil {         // python 的None
        fmt.Println("s2不能初始化,不能使用")
        return
    }
    s2[0] = "lqz" // s2沒有初始化,報錯  index out of range [0] with length 0
    fmt.Println(s2)

    // 使用切片 s1[0]---》報index out of range [0] with length 0--》兩個原因:1 nil 2 長度不夠

    // 6 切片的長度和容量
    // 長度:切片放了幾個值
    // 容量:切片總共能放幾個值
    var a1 [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    var s3 []int = a1[2:3]
    fmt.Println(s3)      // [6 7]  前閉後開區間
    fmt.Println(len(s3)) // len 長度 2
    fmt.Println(cap(s3)) // cap 容量 5 切片基於底層陣列,容量取決於底層陣列--》從切片起始位置到陣列最後

    var s4 = make([]int, 2, 4)
    fmt.Println(len(s4)) // 2
    fmt.Println(cap(s4)) // 4

    // 7 追加切片元素
    // s4 長度是2,容量是4,還能追加倆值
    s4 = append(s4, 99) // 內建函式追加--->s4不是物件
    fmt.Println(s4)
    fmt.Println(len(s4)) // 3
    fmt.Println(cap(s4)) // 4
    s4 = append(s4, 88)  // 內建函式追加--->s4不是物件
    fmt.Println(s4)
    fmt.Println(len(s4)) // 4
    fmt.Println(cap(s4)) // 4
    // 超過容量,再追加---》不會報錯---》
    s4 = append(s4, 77) // 內建函式追加--->s4不是物件
    fmt.Println(s4)
    fmt.Println(len(s4)) // 5
    fmt.Println(cap(s4)) // 8---》翻倍擴容

    s4 = append(s4, 66, 55, 44, 33) //
    fmt.Println(s4)
    fmt.Println(len(s4)) // 9
    fmt.Println(cap(s4)) // 16---》翻倍擴容

    s4 = append(s4, 22, 11, 12, 13, 11, 12) //
    fmt.Println(s4)
    fmt.Println(len(s4)) // 16
    fmt.Println(cap(s4)) // 16---》翻倍擴容-->32

    //8 切片的函式傳遞  切片是引用型別
    test001(s4)            // [0 0 99 88 77 66 55 44 33 22 11 12 13 11 12 13]
    fmt.Println("外部:", s4) // 函式內修改,會影響原來的值

    // 很深的坑: s4 長度和容量 都是16 當引數傳到函式中,修改,會不會影響原來的?
    test002(s4)
    fmt.Println("外部:", s4) // 函式內修改,會影響原來的值
    /*
        1 因為切片依賴於底層陣列
        2 底層陣列的改變會影響切片,切片的改變也會影響底層陣列
        3 當使用append追加上,如果超過了切片容量--》切片會擴容--》重新建立一個陣列--》把原來的值複製過去--》容量翻倍了--》切片指向新的陣列
        4 如果當引數傳遞,再函式中,使用append
            1 超過容量,只要超過後,再修改某個位置的值,就不會相互影響了
            2 如果沒有超過容量,再修改某個位置的值,會相互影響了
    */

    // 9 多維切片
    var s5 [][]int = make([][]int, 2, 3)
    //s5[0]=1
    s5[0] = make([]int, 3) // 內層切片使用,也要初始化
    s5[0][0] = 99          // 報錯了---》內層的切片,沒有透過make初始化出來,不能用
    fmt.Println(s5)

    var s6 [][3]int = make([][3]int, 2, 3)
    fmt.Println(s6[0])
    s6[0][0] = 99
    fmt.Println(s6)

    // 多維切片,第n層,要不要初始化,取決於第n層是什麼型別

    // 定義並賦初值
    var s7 [][]string = [][]string{{"lqz", "小紅"}, {"asfd"}, {"111", "22", "333", "444"}}
    //var s7 [][4]string = [][4]string{{"lqz", "小紅"}, {"asfd"}, {"111", "22", "333", "444"}}
    fmt.Println(s7[0][1])
    fmt.Println(len(s7[2])) // 1
    fmt.Println(cap(s7[2])) // 1
    //fmt.Println(s7[2][4])   // 越界了
    //fmt.Println(s7[0][2]) // 陣列不會越界,切片就會越界
    s7[0] = append(s7[0], "")
    fmt.Println(len(s7[0])) // 3
    fmt.Println(cap(s7[0])) // 4

    // 10 記憶體最佳化  copy函式
    var a3 [100]int
    fmt.Println(a3)
    var s8 []int = a3[0:3]
    fmt.Println(s8)
    s8[0] = 88
    fmt.Println(s8)
    fmt.Println(a3)
    a3[1] = 66
    fmt.Println(s8)
    fmt.Println(a3)

    //s8 複製到小一點的切片上(底層陣列小)  ---》可以大可以小
    var s9 = make([]int, 5, 5)
    copy(s9, s8)
    fmt.Println(s9) // 長度和容量,都是3--》以後操作s9-->傳參,修改---》底層陣列只有3的大小---》記憶體空間佔用小

}

func test001(s []int) {
    s[0] = 999
    fmt.Println(s)
}

func test002(s []int) {
    s[0] = 999
    fmt.Println(s)
    // 如果,再內部,append--——》追加--》超過容量--》放棄掉底層陣列---》新建立一個新陣列--》指向新陣列
    s = append(s, 666)
    fmt.Println("內部", s)
    s[0] = 888
    fmt.Println("內部", s)

}

可變長引數

package main

import "fmt"

// 可變長引數
func main() {
    // *  ** 放在形參上
    // *:接收任意長度的位置引數
    // ** 接收任意長度的關鍵字引數

    // * ** 放在實參上--》* 只能放在列表上,  ** 只能放在字典上

    // go 只有按位置傳引數---》沒有按關鍵字這種用法
    test003(33, 44, "9")

    // 傳任意長度引數
    //fmt.Println(1,2,3,4,5,"dafas")

    test004("11", "22", "14", "15", "16", "17", "18", "19")

    var ss1 [5]string = [5]string{"lqz", "彭于晏", "劉亦菲"}
    test004(ss1[:]...) // 打散了傳入

    // 思考題
    welcome := []string{"hello", "world"} // 長度是2,容量是2
    //welcome := make([]string,3,4) //
    change(welcome...)   // 內部本質就是把 welcome 給了 s
    fmt.Println(welcome) // {"Go", "world"}
}

func test003(a, b int, c string) {
    fmt.Println(a, b, c)
}

func test004(a ...string) {
    fmt.Println(a) // 可以傳任意長度字串===>a是什麼型別?字串型別? 字串切片型別 []string
    fmt.Printf("a的型別是:%T", a)
    //for i := 0; i < len(a); i++ {
    //    fmt.Println(a[i])
    //}

    for _, value := range a {
        fmt.Println(value)

    }
}

func change(s ...string) {
    s[0] = "Go"
    s = append(s, "playground")
    fmt.Println(s) // [Go world  playground]
    s[0] = "lqz"
    fmt.Println(s) // [lqz  world  playground]
}

map

# 陣列,切片,map  容器型別

# map 是在 Go 中將值(value)與鍵(key)關聯的內建型別。透過相應的鍵可以獲取到值

如何建立 map
給 map 新增元素
獲取 map 中的元素
刪除 map 中的元素
獲取 map 的長度
Map 是引用型別
Map 的相等性

hash衝突

# python中的字典 
# go中得map
# redis中得 hash
# java中得 HashMap,TreeMap

# 1  就是根據key即經過一個函式f(key)得到的結果的作為地址去存放當前的key value鍵值對(這個是hashmap的存值方式),但是卻發現算出來的地址上已經有人先來了

就是相當於你在蹲坑,又有人進來了,但是隻有一個坑,就產生了衝突,需要開闢新的空間去讓另一個人有地方上廁所


# 2 解決hash衝突
    1 開放定址法
        1.1  線性探測
        按順序決定值時,如果某資料的值已經存在,則在原來值的基礎上往後加一個單位,直至不發生雜湊衝突。
        就是在此空間不足時,直接放入此空間的後一個空的空間
        1.2 偽隨機探測
            
        
    2 再雜湊法(Rehashing):
        使用另一個雜湊函式處理衝突
    
    3 鏈地址法(java的hashmap):
        - 對於相同的值,使用連結串列進行連線。使用陣列儲存每一個連結串列

    4 建立公共溢位區