您的位置:首頁 > 軟件教程 > 教程 > 【建議收藏】Go語言關(guān)鍵知識點(diǎn)總結(jié)

【建議收藏】Go語言關(guān)鍵知識點(diǎn)總結(jié)

來源:好特整理 | 時(shí)間:2024-06-30 11:47:51 | 閱讀:60 |  標(biāo)簽: 知識 GO   | 分享到:

容器 數(shù)組和切片 在Go語言中,數(shù)組和切片是兩個(gè)基本的數(shù)據(jù)結(jié)構(gòu),用于存儲和操作一組元素。它們有一些相似之處,但也有許多不同之處。下面我們詳細(xì)介紹數(shù)組和切片的特點(diǎn)、用法以及它們之間的區(qū)別。 數(shù)組 數(shù)組是固定長度的序列,存儲相同類型的元素。數(shù)組的長度在定義時(shí)就固定下來,不能改變。 package mai

容器

數(shù)組和切片

在Go語言中,數(shù)組和切片是兩個(gè)基本的數(shù)據(jù)結(jié)構(gòu),用于存儲和操作一組元素。它們有一些相似之處,但也有許多不同之處。下面我們詳細(xì)介紹數(shù)組和切片的特點(diǎn)、用法以及它們之間的區(qū)別。

數(shù)組

數(shù)組是固定長度的序列,存儲相同類型的元素。數(shù)組的長度在定義時(shí)就固定下來,不能改變。

package main

import "fmt"

func main() {
    // 定義一個(gè)長度為5的整型數(shù)組
    var arr [5]int
    fmt.Println(arr) // 輸出: [0 0 0 0 0]

    // 定義并初始化一個(gè)長度為5的整型數(shù)組
    arr2 := [5]int{1, 2, 3, 4, 5}
    fmt.Println(arr2) // 輸出: [1 2 3 4 5]

    // 讓編譯器推斷數(shù)組長度
    arr3 := [...]int{1, 2, 3}
    fmt.Println(arr3) // 輸出: [1 2 3]
}

可以使用索引來訪問和修改數(shù)組中的元素:

package main

import "fmt"

func main() {
    arr := [3]int{1, 2, 3}
    fmt.Println(arr[0]) // 輸出: 1

    arr[1] = 10
    fmt.Println(arr) // 輸出: [1 10 3]
}

可以使用for循環(huán)來遍歷數(shù)組:

package main

import "fmt"

func main() {
    arr := [3]int{1, 2, 3}
    for i, v := range arr {
        fmt.Println(i, v)
    }
}

切片

切片是動(dòng)態(tài)數(shù)組,可以按需增長。切片由三個(gè)部分組成:指針、長度和容量。指針指向數(shù)組中切片的起始位置,長度是切片中的元素個(gè)數(shù),容量是從切片起始位置到數(shù)組末尾的元素個(gè)數(shù)。

package main

import "fmt"

func main() {
    // 創(chuàng)建一個(gè)長度和容量為3的整型切片
    slice := make([]int, 3)
    fmt.Println(slice) // 輸出: [0 0 0]

    // 定義并初始化一個(gè)切片
    slice2 := []int{1, 2, 3, 4, 5}
    fmt.Println(slice2) // 輸出: [1 2 3 4 5]
}

切片可以通過數(shù)組或另一個(gè)切片生成:

package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}
    slice := arr[1:4]
    fmt.Println(slice) // 輸出: [2 3 4]
}

可以使用內(nèi)置的append函數(shù)向切片追加元素:

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3}
    slice = append(slice, 4, 5)
    fmt.Println(slice) // 輸出: [1 2 3 4 5]
}

其他操作和數(shù)組基本一樣,下面再說下數(shù)組和切片的區(qū)別:

  1. 長度
    • 數(shù)組的長度是固定的,定義后不能改變。
    • 切片的長度是動(dòng)態(tài)的,可以通過append函數(shù)增加元素。
  2. 靈活性
    • 數(shù)組在使用上較為僵化,因?yàn)殚L度固定,適用于元素?cái)?shù)量已知且固定的場景。
    • 切片更加靈活,適用于需要?jiǎng)討B(tài)添加或刪除元素的場景。
  3. 性能
    • 數(shù)組的訪問速度通常比切片快,因?yàn)樗鼈兪枪潭ù笮〉,編譯器可以進(jìn)行更多的優(yōu)化。
    • 切片在性能上稍遜,但由于其靈活性,使用更加廣泛。

container包

在Go語言的標(biāo)準(zhǔn)庫中,container包提供了三種常見的數(shù)據(jù)結(jié)構(gòu): (heap)、 雙向鏈表 (list)和 環(huán)形隊(duì)列 (ring)。這些數(shù)據(jù)結(jié)構(gòu)為開發(fā)者提供了高效的插入、刪除和訪問操作。下面我們詳細(xì)介紹這三個(gè)數(shù)據(jù)結(jié)構(gòu)及其用法。

head

heap 包實(shí)現(xiàn)了堆數(shù)據(jù)結(jié)構(gòu)。堆是一種特殊的樹狀結(jié)構(gòu),可以用于實(shí)現(xiàn)優(yōu)先隊(duì)列。
要使用 container/heap 包,必須定義一個(gè)實(shí)現(xiàn) heap.Interface 接口的類型。該接口包含以下方法:

  • Len() int:返回元素?cái)?shù)量。
  • Less(i, j int) bool:報(bào)告索引 i 處的元素是否小于索引 j 處的元素。
  • Swap(i, j int):交換索引 i 和 j 處的元素。
  • Push(x interface{}):將元素 x 添加到堆中。
  • Pop() interface{}:移除并返回堆中的最小元素。

這些方法要求用戶明確實(shí)現(xiàn)堆的各種操作,增加了使用的復(fù)雜度。

package main

import (
    "container/heap"
    "fmt"
)

// 定義一個(gè)實(shí)現(xiàn) heap.Interface 的類型
type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x interface{}) {
    *h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

func main() {
    h := &IntHeap{2, 1, 5}
    heap.Init(h)
    heap.Push(h, 3)
    fmt.Printf("最小元素: %d\n", (*h)[0])
    for h.Len() > 0 {
        fmt.Printf("%d ", heap.Pop(h))
    }
    // 輸出: 最小元素: 1
    //      1 2 3 5
}

list

list 包實(shí)現(xiàn)了雙向鏈表(doubly linked list)。雙向鏈表允許高效的插入和刪除操作。

package main

import (
    "container/list"
    "fmt"
)

func main() {
    l := list.New()

    // 在鏈表前插入元素
    l.PushFront(1)
    l.PushFront(2)

    // 在鏈表后插入元素
    l.PushBack(3)

    // 遍歷鏈表
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }
    // 輸出:
    // 2
    // 1
    // 3
}

ring

ring 包實(shí)現(xiàn)了環(huán)形隊(duì)列(circular list)。環(huán)形隊(duì)列是一種首尾相連的隊(duì)列結(jié)構(gòu)。

package main

import (
    "container/ring"
    "fmt"
)

func main() {
    // 創(chuàng)建一個(gè)長度為3的環(huán)
    r := ring.New(3)

    // 初始化環(huán)中的值
    for i := 0; i < r.Len(); i++ {
        r.Value = i
        r = r.Next()
    }

    // 遍歷環(huán)中的元素
    r.Do(func(p interface{}) {
        fmt.Println(p.(int))
    })
    // 輸出:
    // 0
    // 1
    // 2
}

Channel

什么是Channel

在Go語言中,channel是用于在不同的goroutine之間進(jìn)行通信的機(jī)制。它可以讓一個(gè)goroutine將值發(fā)送到一個(gè)通道中,另一個(gè)goroutine從通道中接收值。channel的設(shè)計(jì)使得goroutine之間的通信和同步變得簡潔而高效。

創(chuàng)建Channel

創(chuàng)建一個(gè)channel使用make函數(shù),指定其傳遞的值的類型:

ch := make(chan int)

可以創(chuàng)建帶緩沖的channel,緩沖大小在make時(shí)指定:

ch := make(chan int, 100)

發(fā)送和接收

發(fā)送和接收操作使用箭頭符號<-:

ch <- 1   // 發(fā)送值1到channel
value := <-ch  // 從channel接收值并賦值給變量value

關(guān)閉Channel

channel可以被主動(dòng)關(guān)閉,關(guān)閉channel使用close函數(shù):

close(ch)

一旦一個(gè)channel被關(guān)閉,再往該channel發(fā)送值會(huì)導(dǎo)致panic,從已關(guān)閉的channel接收值將立即返回該類型的零值并且不會(huì)阻塞(如果通道里還存在未被接收的元素,這些元素也會(huì)正常返回,直到所有元素都被接收,才會(huì)開始返回零值)。

其他操作

無緩沖通道(緩沖大小為0)

  • 發(fā)送操作會(huì)阻塞直到有g(shù)oroutine來接收這個(gè)值。
  • 接收操作會(huì)阻塞直到有值被發(fā)送到channel。

緩沖通道

  • 發(fā)送操作會(huì)在緩沖區(qū)滿時(shí)阻塞。
  • 接收操作會(huì)在緩沖區(qū)為空時(shí)阻塞。

Select語句

select語句可以用于處理多個(gè)channel操作。它會(huì)阻塞直到其中一個(gè)channel可以進(jìn)行操作。select語句中的各個(gè)分支是隨機(jī)選擇的:

select {
case val := <-ch1:
    fmt.Println("Received", val)
case ch2 <- 1:
    fmt.Println("Sent 1")
default:
    fmt.Println("No communication")
}

示例

基于channel,實(shí)現(xiàn)一個(gè)簡單的生產(chǎn)者-消費(fèi)者模型:

package main

import (
    "fmt"
    "time"
)

func producer(ch chan int) {
    //循環(huán)往通道發(fā)送5個(gè)元素,間隔1秒
    for i := 0; i < 5; i++ {
        fmt.Println("Producing", i)
        ch <- i
        time.Sleep(time.Second)
    }
    //發(fā)送完所有消息后關(guān)閉通道
    close(ch)
}

func consumer(ch chan int) {
    //可以通過range遍歷通道的元素
    //因?yàn)樯a(chǎn)者已經(jīng)關(guān)閉了通道,所以遍歷完所有元素后,循環(huán)會(huì)自己退出
    for val := range ch {
        fmt.Println("Consuming", val)
        time.Sleep(time.Second)
    }
}

func main() {
    ch := make(chan int, 2)
    go producer(ch)
    consumer(ch)
}

常見問題

  1. 避免在接收端關(guān)閉通道 :通常由發(fā)送方負(fù)責(zé)關(guān)閉channel。
  2. 避免重復(fù)關(guān)閉通道 :多次關(guān)閉同一個(gè)channel會(huì)導(dǎo)致panic。
  3. 避免從未使用的通道發(fā)送和接收 :未使用的channel操作會(huì)導(dǎo)致死鎖。比如只接收,沒發(fā)送,程序會(huì)一直阻塞在接收處。

函數(shù)

在Go語言中,函數(shù)是一等公民(first-class citizen),這意味著函數(shù)可以像其他類型(例如整數(shù)、字符串等)一樣使用和操作。這一特性使得函數(shù)的使用非常靈活和強(qiáng)大。具體來說,函數(shù)作為一等公民具有以下特點(diǎn):

函數(shù)可以賦值給變量

你可以將一個(gè)函數(shù)賦值給一個(gè)變量,這樣就可以通過這個(gè)變量來調(diào)用函數(shù):

package main

import "fmt"

func main() {
    add := func(a, b int) int {
        return a + b
    }
    fmt.Println(add(3, 4)) // 輸出: 7
}

函數(shù)可以作為參數(shù)傳遞給另一個(gè)函數(shù)

函數(shù)可以作為參數(shù)傳遞給其他函數(shù),這使得可以實(shí)現(xiàn)高階函數(shù):

package main

import "fmt"

func applyOperation(a, b int, op func(int, int) int) int {
    return op(a, b)
}

func main() {
    add := func(a, b int) int {
        return a + b
    }
    result := applyOperation(5, 3, add)
    fmt.Println(result) // 輸出: 8
}

函數(shù)可以作為返回值從另一個(gè)函數(shù)返回

函數(shù)可以從另一個(gè)函數(shù)返回,這使得可以動(dòng)態(tài)生成函數(shù):

package main

import "fmt"

func createMultiplier(factor int) func(int) int {
    return func(x int) int {
        return x * factor
    }
}

func main() {
    double := createMultiplier(2)
    triple := createMultiplier(3)
    fmt.Println(double(4)) // 輸出: 8
    fmt.Println(triple(4)) // 輸出: 12
}

函數(shù)可以嵌套定義

在Go語言中,可以在函數(shù)內(nèi)部定義另一個(gè)函數(shù):

package main

import "fmt"

func main() {
    outer := func() {
        fmt.Println("This is the outer function.")

        inner := func() {
            fmt.Println("This is the inner function.")
        }

        inner()
    }

    outer()
}

函數(shù)可以作為匿名函數(shù)

匿名函數(shù)是一種無需命名的函數(shù),可以直接使用:

package main

import "fmt"

func main() {
    result := func(a, b int) int {
        return a + b
    }(3, 5)
    
    fmt.Println(result) // 輸出: 8
}

閉包(Closures)

Go語言支持閉包,閉包是一個(gè)函數(shù),這個(gè)函數(shù)可以捕獲并記住其所在環(huán)境的變量:

package main

import "fmt"

func main() {
    x := 10

    // 定義一個(gè)修改外部變量x的閉包
    closure := func() int {
        x += 1
        return x
    }

    fmt.Println(closure()) // 輸出: 11
    fmt.Println(x)         // 輸出: 11
}

package main

import "fmt"

func main() {
    counter := func() func() int {
        count := 0
        return func() int {
            count++
            return count
        }
    }()
    
    fmt.Println(counter()) // 輸出: 1
    fmt.Println(counter()) // 輸出: 2
    fmt.Println(counter()) // 輸出: 3
}

錯(cuò)誤處理

Go語言中的錯(cuò)誤處理方式不同于傳統(tǒng)的異常處理機(jī)制。它采用了明確的、基于值的錯(cuò)誤處理方法。每個(gè)函數(shù)可以返回一個(gè)錯(cuò)誤值來表示是否出現(xiàn)了問題。

基本錯(cuò)誤處理

Go語言中使用內(nèi)置的error接口類型來表示錯(cuò)誤。error接口定義如下:

type error interface {
    Error() string
}

函數(shù)通常返回一個(gè)error類型的值來表示操作是否成功。如果沒有錯(cuò)誤,返回nil。

package main

import (
    "errors"
    "fmt"
)

// 定義一個(gè)函數(shù),返回錯(cuò)誤
func divide(a, b int) (int, error) {
    if b == 0 {
        //如果有問題,通過New方法新建一個(gè)錯(cuò)誤信息
        return 0, errors.New("division by zero")
    }
    //如果沒有錯(cuò)誤返回nil
    return a / b, nil
}

func main() {
    result, err := divide(4, 2)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }

    result, err = divide(4, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

自定義錯(cuò)誤類型

除了使用errors.New創(chuàng)建簡單錯(cuò)誤外,Go語言允許我們定義自己的錯(cuò)誤類型,實(shí)現(xiàn)更豐富的錯(cuò)誤信息。

package main

import (
    "fmt"
)

// 自定義錯(cuò)誤類型
type MyError struct {
    Code    int
    Message string
}

// 實(shí)現(xiàn)error接口的Error方法
func (e *MyError) Error() string {
    return fmt.Sprintf("Code: %d, Message: %s", e.Code, e.Message)
}

// 定義一個(gè)函數(shù),返回自定義錯(cuò)誤(只要實(shí)現(xiàn)了Error()方法,就可以直接返回error類型)
func doSomething(flag bool) error {
    if !flag {
        return &MyError{Code: 123, Message: "something went wrong"}
    }
    return nil
}

func main() {
    err := doSomething(false)
    if err != nil {
        fmt.Println("Error:", err)
        
        // 類型斷言,獲取具體的錯(cuò)誤類型
        if myErr, ok := err.(*MyError); ok {
            fmt.Println("Custom Error Code:", myErr.Code)
        }
    }
}

異常處理機(jī)制

Go語言也有類似異常的處理機(jī)制,即defer、panic和recover,但它們主要用于處理程序中不可恢復(fù)的錯(cuò)誤。

  • defer :用于延遲執(zhí)行一個(gè)函數(shù),在函數(shù)返回前執(zhí)行。如果一個(gè)函數(shù)里面有多個(gè)defer語句,寫在最后面的defer最先執(zhí)行。
  • panic :意料之外的錯(cuò)誤,也可以手動(dòng)調(diào)用。如果panic沒有處理,程序會(huì)終止。
  • recover :恢復(fù)panic,并停止程序終止的過程。
package main

import "fmt"

func main() {
    defer func() {
        //使用defer執(zhí)行一個(gè)匿名函數(shù),確保recover一定能執(zhí)行
        if r := recover(); r != nil {
            //恢復(fù)panic,此處可以進(jìn)行異常處理,比如打印日志
            fmt.Println("Recovered from panic:", r)
        }
    }()

    fmt.Println("Starting the program")
    //手動(dòng)觸發(fā)一個(gè)panic
    panic("Something went wrong!")
    fmt.Println("This line will not be executed")
}
小編推薦閱讀

好特網(wǎng)發(fā)布此文僅為傳遞信息,不代表好特網(wǎng)認(rèn)同期限觀點(diǎn)或證實(shí)其描述。

Go v1.62
Go v1.62
類型:動(dòng)作冒險(xiǎn)  運(yùn)營狀態(tài):正式運(yùn)營  語言:中文   

游戲攻略

游戲禮包

游戲視頻

游戲下載

游戲活動(dòng)

GoEscape是一款迷宮逃脫休閑闖關(guān)游戲。在這款游戲中,玩家可以挑戰(zhàn)大量關(guān)卡,通過旋轉(zhuǎn)屏幕的方式幫助球球

相關(guān)視頻攻略

更多

掃二維碼進(jìn)入好特網(wǎng)手機(jī)版本!

掃二維碼進(jìn)入好特網(wǎng)微信公眾號!

本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]

湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)