Go中make函數(shù)和append函數(shù)的作用詳解
Go 中的 make 函數(shù)和 append 函數(shù)
make 函數(shù)的作用
make函數(shù)專門用于創(chuàng)建和初始化 slice、map 和 channel 這三種內(nèi)建引用類型- 對于 slice,可以指定長度和容量;對于 map,可以指定初始容量;對于 channel,可以指定緩沖區(qū)大小
make返回的是已初始化的類型引用,可以直接使用- 合理使用預分配容量可以顯著提高程序性能,特別是在處理大量數(shù)據(jù)時
- 與
new函數(shù)不同,make會執(zhí)行完整的初始化過程,而不僅僅是內(nèi)存分配
append 函數(shù)的作用
append函數(shù)用于向切片(slice)添加元素- 可以添加單個元素或多個元素
- 可以合并兩個切片
- 自動處理切片的擴容機制
- 是操作切片最常用的函數(shù)之一
1. make 函數(shù)的基本概念
make 是 Go 語言中的一個內(nèi)置函數(shù),主要用于創(chuàng)建并初始化以下三種內(nèi)建的引用類型:
- slice(切片)
- map(映射)
- channel(通道)
與 new 函數(shù)不同,make 不僅分配內(nèi)存,還會進行初始化操作,返回的是類型的引用(而不是指針)。
2. make 函數(shù)的語法格式
make(T, args...)
其中:
T:要創(chuàng)建的類型(slice、map 或 channel)args...:根據(jù)類型不同而變化的參數(shù)
3. 用于不同數(shù)據(jù)類型的詳細用法
3.1 使用 make 創(chuàng)建切片(slice)
語法:
make([]T, length, capacity)
參數(shù)說明:
T:切片元素的類型length:切片的長度(當前包含的元素個數(shù))capacity:切片的容量(可選參數(shù),默認等于 length)
示例:
package main
import "fmt"
func main() {
// 創(chuàng)建長度為 3,容量為 5 的整型切片
slice1 := make([]int, 3, 5)
fmt.Printf("slice1: %v, len: %d, cap: %d\n", slice1, len(slice1), cap(slice1))
// 輸出: slice1: [0 0 0], len: 3, cap: 5
// 創(chuàng)建長度和容量都為 3 的字符串切片
slice2 := make([]string, 3)
fmt.Printf("slice2: %v, len: %d, cap: %d\n", slice2, len(slice2), cap(slice2))
// 輸出: slice2: [ ], len: 3, cap: 3
// 不指定容量,容量默認等于長度
slice3 := make([]int, 3)
fmt.Printf("slice3: %v, len: %d, cap: %d\n", slice3, len(slice3), cap(slice3))
// 輸出: slice3: [0 0 0], len: 3, cap: 3
}注意事項:
- 使用
make創(chuàng)建的切片會被自動初始化為元素類型的零值 - 容量必須大于等于長度
3.2 使用 make 創(chuàng)建映射(map)
語法:
make(map[K]V, initialCapacity)
參數(shù)說明:
K:鍵的類型V:值的類型initialCapacity:初始容量(可選參數(shù))
示例:
package main
import "fmt"
func main() {
// 創(chuàng)建字符串到整型的映射,不指定初始容量
map1 := make(map[string]int)
map1["apple"] = 5
map1["banana"] = 3
fmt.Printf("map1: %v\n", map1)
// 輸出: map1: map[apple:5 banana:3]
// 創(chuàng)建字符串到字符串的映射,指定初始容量為 10
map2 := make(map[string]string, 10)
map2["name"] = "Alice"
map2["city"] = "Beijing"
fmt.Printf("map2: %v\n", map2)
// 輸出: map2: map[city:Beijing name:Alice]
fmt.Printf("map1 len: %d\n", len(map1))
fmt.Printf("map2 len: %d\n", len(map2))
}注意事項:
- 初始容量只是提示性的,映射會根據(jù)需要自動擴容
- 使用
make創(chuàng)建的映射是空的,可以立即進行鍵值對操作
3.3 使用 make 創(chuàng)建通道(channel)
語法:
make(chan T, bufferSize)
參數(shù)說明:
T:通道傳輸?shù)臄?shù)據(jù)類型bufferSize:緩沖區(qū)大小(可選參數(shù))
示例:
package main
import (
"fmt"
"time"
)
func main() {
// 創(chuàng)建無緩沖的整型通道
ch1 := make(chan int)
// 創(chuàng)建緩沖大小為 3 的字符串通道
ch2 := make(chan string, 3)
// 使用無緩沖通道的示例
go func() {
time.Sleep(1 * time.Second)
ch1 <- 42 // 發(fā)送數(shù)據(jù)
}()
// 使用有緩沖通道的示例
ch2 <- "hello"
ch2 <- "world"
ch2 <- "golang"
fmt.Printf("ch2 len: %d, cap: %d\n", len(ch2), cap(ch2))
// 輸出: ch2 len: 3, cap: 3
// 從無緩沖通道接收數(shù)據(jù)
value := <-ch1
fmt.Printf("Received from ch1: %d\n", value)
// 輸出: Received from ch1: 42
// 從有緩沖通道接收數(shù)據(jù)
fmt.Printf("Received from ch2: %s\n", <-ch2)
fmt.Printf("Received from ch2: %s\n", <-ch2)
fmt.Printf("Received from ch2: %s\n", <-ch2)
}注意事項:
- 無緩沖通道(bufferSize=0 或省略)是同步的,發(fā)送和接收操作會阻塞直到另一端準備好
- 有緩沖通道是異步的,只有在緩沖區(qū)滿時發(fā)送才會阻塞,緩沖區(qū)空時接收才會阻塞
4. make 與 new 的區(qū)別
| 特性 | make | new |
|---|---|---|
| 適用類型 | slice、map、channel | 任意類型 |
| 返回值 | 類型的引用(T) | 類型的指針(*T) |
| 初始化 | 會進行初始化 | 只分配零值內(nèi)存 |
| 零值 | 返回已初始化的值 | 返回指向零值的指針 |
示例對比:
package main
import "fmt"
func main() {
// 使用 make 創(chuàng)建切片
slice1 := make([]int, 3)
fmt.Printf("make slice: %v, type: %T\n", slice1, slice1)
// 輸出: make slice: [0 0 0], type: []int
// 使用 new 創(chuàng)建切片
slice2 := new([]int)
fmt.Printf("new slice: %v, type: %T\n", slice2, slice2)
// 輸出: new slice: &[], type: *[]int
// 使用 make 創(chuàng)建映射
map1 := make(map[string]int)
map1["key"] = 1
fmt.Printf("make map: %v, type: %T\n", map1, map1)
// 輸出: make map: map[key:1], type: map[string]int
// 使用 new 創(chuàng)建映射
map2 := new(map[string]int)
// (*map2)["key"] = 1 // 這會導致 panic,因為映射未初始化
fmt.Printf("new map: %v, type: %T\n", map2, map2)
// 輸出: new map: &map[], type: *map[string]int
}6. append 函數(shù)
6.1 append 函數(shù)的基本概念
append 是 Go 語言中的一個內(nèi)置函數(shù),專門用于向切片(slice)添加元素。它是操作切片最常用的函數(shù)之一。
6.2 append 函數(shù)的語法格式
append(slice []T, elements ...T) []T
參數(shù)說明:
slice:目標切片elements:要添加的元素(可以是單個元素或多個元素)- 返回值:新的切片(可能指向新的底層數(shù)組)
6.3 append 函數(shù)的使用方法
6.3.1 添加單個元素
package main
import "fmt"
func main() {
// 創(chuàng)建空切片
slice := make([]int, 0, 5)
// 添加單個元素
slice = append(slice, 1)
slice = append(slice, 2)
slice = append(slice, 3)
fmt.Printf("切片內(nèi)容: %v, 長度: %d, 容量: %d\n", slice, len(slice), cap(slice))
// 輸出: 切片內(nèi)容: [1 2 3], 長度: 3, 容量: 5
}6.3.2 添加多個元素
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
// 添加多個元素
slice = append(slice, 4, 5, 6)
fmt.Printf("切片內(nèi)容: %v, 長度: %d, 容量: %d\n", slice, len(slice), cap(slice))
// 輸出: 切片內(nèi)容: [1 2 3 4 5 6], 長度: 6, 容量: 6
}6.3.3 合并兩個切片
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}
// 合并兩個切片(注意使用 ... 展開操作符)
slice1 = append(slice1, slice2...)
fmt.Printf("合并后的切片: %v\n", slice1)
// 輸出: 合并后的切片: [1 2 3 4 5 6]
}6.3.4 在指定位置插入元素
package main
import "fmt"
func main() {
slice := []int{1, 2, 4, 5}
// 在索引2的位置插入元素3
index := 2
element := 3
// 方法:使用 append 和切片的組合
slice = append(slice[:index], append([]int{element}, slice[index:]...)...)
fmt.Printf("插入后的切片: %v\n", slice)
// 輸出: 插入后的切片: [1 2 3 4 5]
// 更簡潔的插入方法
slice2 := []int{1, 2, 4, 5}
slice2 = append(slice2, 0) // 先擴展切片
copy(slice2[index+1:], slice2[index:]) // 移動元素
slice2[index] = element // 插入新元素
fmt.Printf("插入后的切片2: %v\n", slice2)
// 輸出: 插入后的切片2: [1 2 3 4 5]
}6.4 append 函數(shù)的擴容機制
package main
import "fmt"
func main() {
// 演示 append 的擴容過程
slice := make([]int, 0, 2)
fmt.Printf("初始狀態(tài) - 長度: %d, 容量: %d\n", len(slice), cap(slice))
for i := 1; i <= 10; i++ {
slice = append(slice, i)
fmt.Printf("添加 %d - 長度: %d, 容量: %d\n", i, len(slice), cap(slice))
}
/* 輸出示例:
初始狀態(tài) - 長度: 0, 容量: 2
添加 1 - 長度: 1, 容量: 2
添加 2 - 長度: 2, 容量: 2
添加 3 - 長度: 3, 容量: 4 (第一次擴容)
添加 4 - 長度: 4, 容量: 4
添加 5 - 長度: 5, 容量: 8 (第二次擴容)
添加 6 - 長度: 6, 容量: 8
添加 7 - 長度: 7, 容量: 8
添加 8 - 長度: 8, 容量: 8
添加 9 - 長度: 9, 容量: 16 (第三次擴容)
添加 10 - 長度: 10, 容量: 16
*/
}6.5 append 函數(shù)的注意事項
6.5.1 返回值必須接收
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
// 錯誤:append 的返回值必須接收
// append(slice, 4) // 這樣不會修改原切片
// 正確:接收返回值
slice = append(slice, 4)
fmt.Printf("正確使用: %v\n", slice)
// 輸出: 正確使用: [1 2 3 4]
}6.5.2 切片共享問題
package main
import "fmt"
func main() {
// 創(chuàng)建底層數(shù)組
arr := [5]int{1, 2, 3, 4, 5}
// 創(chuàng)建兩個共享底層數(shù)組的切片
slice1 := arr[1:4] // [2, 3, 4]
slice2 := slice1[0:2] // [2, 3]
fmt.Printf("修改前 - slice1: %v, slice2: %v, arr: %v\n", slice1, slice2, arr)
// 修改 slice2
slice2[0] = 200
fmt.Printf("修改 slice2 后 - slice1: %v, slice2: %v, arr: %v\n", slice1, slice2, arr)
// 對 slice1 進行 append(可能觸發(fā)擴容)
slice1 = append(slice1, 6)
slice1[0] = 300
fmt.Printf("append slice1 后 - slice1: %v, slice2: %v, arr: %v\n", slice1, slice2, arr)
}6.5.3 性能優(yōu)化建議
package main
import "fmt"
func main() {
// 性能優(yōu)化:預分配容量
// 方法1:使用 make 預分配容量
efficientSlice := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
efficientSlice = append(efficientSlice, i)
}
// 方法2:一次性添加多個元素
var batchSlice []int
for i := 0; i < 1000; i += 100 {
batch := make([]int, 100)
for j := 0; j < 100; j++ {
batch[j] = i + j
}
batchSlice = append(batchSlice, batch...)
}
fmt.Printf("預分配容量 - 長度: %d, 容量: %d\n",
len(efficientSlice), cap(efficientSlice))
fmt.Printf("批量添加 - 長度: %d, 容量: %d\n",
len(batchSlice), cap(batchSlice))
}7. make 和 append 的配合使用
7.1 最佳實踐示例
package main
import "fmt"
func main() {
// 場景:處理大量數(shù)據(jù)時,先預分配容量再使用 append
// 1. 使用 make 預分配容量
expectedSize := 10000
dataSlice := make([]int, 0, expectedSize)
// 2. 使用 append 添加數(shù)據(jù)
for i := 0; i < expectedSize; i++ {
dataSlice = append(dataSlice, i*2)
}
// 3. 處理完成后可以截斷不需要的部分
if len(dataSlice) > 5000 {
dataSlice = dataSlice[:5000]
}
fmt.Printf("最終結(jié)果 - 長度: %d, 容量: %d\n", len(dataSlice), cap(dataSlice))
// 4. 如果需要釋放內(nèi)存,可以重新分配
if cap(dataSlice) > len(dataSlice)*2 {
// 創(chuàng)建新的切片,只保留需要的容量
optimizedSlice := make([]int, len(dataSlice))
copy(optimizedSlice, dataSlice)
dataSlice = optimizedSlice
}
fmt.Printf("優(yōu)化后 - 長度: %d, 容量: %d\n", len(dataSlice), cap(dataSlice))
}7.2 實際應用場景
package main
import (
"fmt"
"strings"
)
func processStrings(words []string) []string {
// 預分配容量
result := make([]string, 0, len(words))
for _, word := range words {
// 過濾和處理字符串
if len(word) > 0 && !strings.Contains(word, "test") {
processed := strings.ToUpper(word)
result = append(result, processed)
}
}
return result
}
func main() {
input := []string{"hello", "world", "test", "go", "programming"}
output := processStrings(input)
fmt.Printf("輸入: %v\n", input)
fmt.Printf("輸出: %v\n", output)
// 輸出: 輸入: [hello world test go programming]
// 輸出: 輸出: [HELLO WORLD GO PROGRAMMING]
}8. 常見使用場景和最佳實踐
8.1 切片的最佳實踐
package main
import "fmt"
func main() {
// 當你知道大概需要多少元素時,預分配容量可以提高性能
expectedSize := 1000
// 好的做法:預分配容量
efficientSlice := make([]int, 0, expectedSize)
for i := 0; i < expectedSize; i++ {
efficientSlice = append(efficientSlice, i)
}
// 不好的做法:讓切片頻繁擴容
var inefficientSlice []int
for i := 0; i < expectedSize; i++ {
inefficientSlice = append(inefficientSlice, i)
}
fmt.Printf("Efficient slice len: %d, cap: %d\n",
len(efficientSlice), cap(efficientSlice))
fmt.Printf("Inefficient slice len: %d, cap: %d\n",
len(inefficientSlice), cap(inefficientSlice))
}8.2 映射的最佳實踐
package main
import "fmt"
func main() {
// 當你知道大概需要存儲多少鍵值對時,預分配容量可以減少重新哈希的次數(shù)
expectedItems := 1000
// 好的做法:預分配容量
efficientMap := make(map[int]string, expectedItems)
for i := 0; i < expectedItems; i++ {
efficientMap[i] = fmt.Sprintf("value%d", i)
}
// 不好的做法:讓映射頻繁擴容
inefficientMap := make(map[int]string)
for i := 0; i < expectedItems; i++ {
inefficientMap[i] = fmt.Sprintf("value%d", i)
}
fmt.Printf("Efficient map size: %d\n", len(efficientMap))
fmt.Printf("Inefficient map size: %d\n", len(inefficientMap))
}到此這篇關(guān)于Go中make函數(shù)和append函數(shù)的作用詳解的文章就介紹到這了,更多相關(guān)go make函數(shù)和append函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言編程中判斷文件是否存在是創(chuàng)建目錄的方法
這篇文章主要介紹了Go語言編程中判斷文件是否存在是創(chuàng)建目錄的方法,示例都是使用os包下的函數(shù),需要的朋友可以參考下2015-10-10
如何在Go中將[]byte轉(zhuǎn)換為io.Reader
本文主要介紹了如何在Go中將[]byte轉(zhuǎn)換為io.Reader,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12

