在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 7.2 切片
4.7 strings 和 strconv 包
13.6 啟動外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫
13 錯誤處理與測試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個例子:使用 Sorter 接口排序
?# 11.5 測試一個值是否實現(xiàn)了某個接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運行時異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實例
12.8 使用接口的實際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說明
7.2 切片
8.0 Map
3.1 Go 開發(fā)環(huán)境的基本要求
5.6 標簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測試和基準測試
6.8 閉包
4.9 指針
13.1 錯誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過 Git 打包和安裝
2.7 Go 運行時(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動態(tài)類型
13.3 從 panic 中恢復(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標識符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對調(diào)
6.11 計算函數(shù)執(zhí)行時間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開發(fā)環(huán)境與其它工具
13.8 測試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語言進行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長參數(shù)
13.9 用(測試數(shù)據(jù))表驅(qū)動測試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫
3.3 調(diào)試器
4.5 基本類型和運算符
?# 11.8 第二個例子:讀和寫
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標簽的結(jié)構(gòu)體
7.5 切片的復制與追加
?# 11.3 類型斷言:如何檢測和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時間和日期
2.3 在 Linux 上安裝 Go
12 讀寫數(shù)據(jù)
6.12 通過內(nèi)存緩存來提升性能
9.1 標準庫概述
12.6 用切片讀寫文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯誤的模式
3.2 編輯器和集成開發(fā)環(huán)境
12.12 Go 中的密碼學
5.2 測試多返回值函數(shù)的錯誤
6.7 將函數(shù)作為參數(shù)
8.2 測試鍵值對是否存在及刪除元素
3.4 構(gòu)建并運行 Go 程序
2.1 平臺與架構(gòu)
5.3 switch 結(jié)構(gòu)

7.2 切片

7.2.1 概念

切片(slice)是對數(shù)組一個連續(xù)片段的引用(該數(shù)組我們稱之為相關(guān)數(shù)組,通常是匿名的),所以切片是一個引用類型(因此更類似于 C/C++ 中的數(shù)組類型,或者 Python 中的 list 類型)。這個片段可以是整個數(shù)組,或者是由起始和終止索引標識的一些項的子集。需要注意的是,終止索引標識的項不包括在切片內(nèi)。切片提供了一個相關(guān)數(shù)組的動態(tài)窗口。

切片是可索引的,并且可以由 len() 函數(shù)獲取長度。

給定項的切片索引可能比相關(guān)數(shù)組的相同元素的索引小。和數(shù)組不同的是,切片的長度可以在運行時修改,最小為 0 最大為相關(guān)數(shù)組的長度:切片是一個 長度可變的數(shù)組。

切片提供了計算容量的函數(shù) cap() 可以測量切片最長可以達到多少:它等于切片的長度 + 數(shù)組除切片之外的長度。如果 s 是一個切片,cap(s) 就是從 s[0] 到數(shù)組末尾的數(shù)組長度。切片的長度永遠不會超過它的容量,所以對于 切片 s 來說該不等式永遠成立:0 <= len(s) <= cap(s)。

多個切片如果表示同一個數(shù)組的片段,它們可以共享數(shù)據(jù);因此一個切片和相關(guān)數(shù)組的其他切片是共享存儲的,相反,不同的數(shù)組總是代表不同的存儲。數(shù)組實際上是切片的構(gòu)建塊。

優(yōu)點 因為切片是引用,所以它們不需要使用額外的內(nèi)存并且比使用數(shù)組更有效率,所以在 Go 代碼中 切片比數(shù)組更常用。

聲明切片的格式是: var identifier []type(不需要說明長度)。

一個切片在未初始化之前默認為 nil,長度為 0。

切片的初始化格式是:var slice1 []type = arr1[start:end]。

這表示 slice1 是由數(shù)組 arr1 從 start 索引到 end-1 索引之間的元素構(gòu)成的子集(切分數(shù)組,start:end 被稱為 slice 表達式)。所以 slice1[0] 就等于 arr1[start]。這可以在 arr1 被填充前就定義好。

如果某個人寫:var slice1 []type = arr1[:] 那么 slice1 就等于完整的 arr1 數(shù)組(所以這種表示方式是 arr1[0:len(arr1)] 的一種縮寫)。另外一種表述方式是:slice1 = &arr1。

arr1[2:]arr1[2:len(arr1)] 相同,都包含了數(shù)組從第三個到最后的所有元素。

arr1[:3]arr1[0:3] 相同,包含了從第一個到第三個元素(不包括第三個)。

如果你想去掉 slice1 的最后一個元素,只要 slice1 = slice1[:len(slice1)-1]。

一個由數(shù)字 1、2、3 組成的切片可以這么生成:s := [3]int{1,2,3}[:] 甚至更簡單的 s := []int{1,2,3}。

s2 := s[:] 是用切片組成的切片,擁有相同的元素,但是仍然指向相同的相關(guān)數(shù)組。

一個切片 s 可以這樣擴展到它的大小上限:s = s[:cap(s)],如果再擴大的話就會導致運行時錯誤(參見第 7.7 節(jié))。

對于每一個切片(包括 string),以下狀態(tài)總是成立的:

s == s[:i] + s[i:] // i是一個整數(shù)且: 0 <= i <= len(s)
len(s) <= cap(s)

切片也可以用類似數(shù)組的方式初始化:var x = []int{2, 3, 5, 7, 11}。這樣就創(chuàng)建了一個長度為 5 的數(shù)組并且創(chuàng)建了一個相關(guān)切片。

切片在內(nèi)存中的組織方式實際上是一個有 3 個域的結(jié)構(gòu)體:指向相關(guān)數(shù)組的指針,切片長度以及切片容量。下圖給出了一個長度為 2,容量為 4 的切片y。

  • y[0] = 3y[1] = 5。
  • 切片 y[0:4] 由 元素 3,5,7 和 11 組成。

http://wiki.jikexueyuan.com/project/the-way-to-go/images/7.2_fig7.2.png?raw=true" alt="" />

示例 7.7 array_slices.go

package main
import "fmt"

func main() {
    var arr1 [6]int
    var slice1 []int = arr1[2:5] // item at index 5 not included!

    // load the array with integers: 0,1,2,3,4,5
    for i := 0; i < len(arr1); i++ {
        arr1[i] = i
    }

    // print the slice
    for i := 0; i < len(slice1); i++ {
        fmt.Printf("Slice at %d is %d\n", i, slice1[i])
    }

    fmt.Printf("The length of arr1 is %d\n", len(arr1))
    fmt.Printf("The length of slice1 is %d\n", len(slice1))
    fmt.Printf("The capacity of slice1 is %d\n", cap(slice1))

    // grow the slice
    slice1 = slice1[0:4]
    for i := 0; i < len(slice1); i++ {
        fmt.Printf("Slice at %d is %d\n", i, slice1[i])
    }
    fmt.Printf("The length of slice1 is %d\n", len(slice1))
    fmt.Printf("The capacity of slice1 is %d\n", cap(slice1))

    // grow the slice beyond capacity
    //slice1 = slice1[0:7 ] // panic: runtime error: slice bound out of range
}

輸出:

Slice at 0 is 2  
Slice at 1 is 3  
Slice at 2 is 4  
The length of arr1 is 6  
The length of slice1 is 3  
The capacity of slice1 is 4  
Slice at 0 is 2  
Slice at 1 is 3  
Slice at 2 is 4  
Slice at 3 is 5  
The length of slice1 is 4  
The capacity of slice1 is 4  

如果 s2 是一個 slice,你可以將 s2 向后移動一位 s2 = s2[1:],但是末尾沒有移動。切片只能向后移動,s2 = s2[-1:] 會導致編譯錯誤。切片不能被重新分片以獲取數(shù)組的前一個元素。

注意 絕對不要用指針指向 slice。切片本身已經(jīng)是一個引用類型,所以它本身就是一個指針!!

問題 7.2: 給定切片 b:= []byte{'g', 'o', 'l', 'a', 'n', 'g'},那么 b[1:4]、b[:2]b[2:]b[:] 分別是什么?

7.2.2 將切片傳遞給函數(shù)

如果你有一個函數(shù)需要對數(shù)組做操作,你可能總是需要把參數(shù)聲明為切片。當你調(diào)用該函數(shù)時,把數(shù)組分片,創(chuàng)建為一個 切片引用并傳遞給該函數(shù)。這里有一個計算數(shù)組元素和的方法:

func sum(a []int) int {
    s := 0
    for i := 0; i < len(a); i++ {
        s += a[i]
    }
    return s
}

func main() {
    var arr = [5]int{0, 1, 2, 3, 4}
    sum(arr[:])
}

7.2.3 用 make() 創(chuàng)建一個切片

當相關(guān)數(shù)組還沒有定義時,我們可以使用 make() 函數(shù)來創(chuàng)建一個切片 同時創(chuàng)建好相關(guān)數(shù)組:var slice1 []type = make([]type, len)。

也可以簡寫為 slice1 := make([]type, len),這里 len 是數(shù)組的長度并且也是 slice 的初始長度。

所以定義 s2 := make([]int, 10),那么 cap(s2) == len(s2) == 10。

make 接受 2 個參數(shù):元素的類型以及切片的元素個數(shù)。

如果你想創(chuàng)建一個 slice1,它不占用整個數(shù)組,而只是占用以 len 為個數(shù)個項,那么只要:slice1 := make([]type, len, cap)。

make 的使用方式是:func make([]T, len, cap),其中 cap 是可選參數(shù)。

所以下面兩種方法可以生成相同的切片:

make([]int, 50, 100)
new([100]int)[0:50]

下圖描述了使用 make 方法生成的切片的內(nèi)存結(jié)構(gòu):http://wiki.jikexueyuan.com/project/the-way-to-go/images/7.2_fig7.2.1.png?raw=true" alt="" />

示例 7.8 make_slice.go

package main
import "fmt"

func main() {
    var slice1 []int = make([]int, 10)
    // load the array/slice:
    for i := 0; i < len(slice1); i++ {
        slice1[i] = 5 * i
    }

    // print the slice:
    for i := 0; i < len(slice1); i++ {
        fmt.Printf("Slice at %d is %d\n", i, slice1[i])
    }
    fmt.Printf("\nThe length of slice1 is %d\n", len(slice1))
    fmt.Printf("The capacity of slice1 is %d\n", cap(slice1))
}

輸出:

Slice at 0 is 0  
Slice at 1 is 5  
Slice at 2 is 10  
Slice at 3 is 15  
Slice at 4 is 20  
Slice at 5 is 25  
Slice at 6 is 30  
Slice at 7 is 35  
Slice at 8 is 40  
Slice at 9 is 45  

The length of slice1 is 10  
The capacity of slice1 is 10  

因為字符串是純粹不可變的字節(jié)數(shù)組,它們也可以被切分成 切片。

練習 7.4: fobinacci_funcarray.go: 為練習 7.3 寫一個新的版本,主函數(shù)調(diào)用一個使用序列個數(shù)作為參數(shù)的函數(shù),該函數(shù)返回一個大小為序列個數(shù)的 Fibonacci 切片。

7.2.4 new() 和 make() 的區(qū)別

看起來二者沒有什么區(qū)別,都在堆上分配內(nèi)存,但是它們的行為不同,適用于不同的類型。

  • new(T) 為每個新的類型T分配一片內(nèi)存,初始化為 0 并且返回類型為*T的內(nèi)存地址:這種方法 返回一個指向類型為 T,值為 0 的地址的指針,它適用于值類型如數(shù)組和結(jié)構(gòu)體(參見第 10 章);它相當于 &T{}。
  • make(T) 返回一個類型為 T 的初始值,它只適用于3種內(nèi)建的引用類型:切片、map 和 channel(參見第 8 章,第 13 章)。

換言之,new 函數(shù)分配內(nèi)存,make 函數(shù)初始化;下圖給出了區(qū)別:

http://wiki.jikexueyuan.com/project/the-way-to-go/images/7.3_fig7.3.png?raw=true" alt="" />

在圖 7.3 的第一幅圖中:

var p *[]int = new([]int) // *p == nil; with len and cap 0
p := new([]int)

在第二幅圖中, p := make([]int, 0) ,切片 已經(jīng)被初始化,但是指向一個空的數(shù)組。

以上兩種方式實用性都不高。下面的方法:

var v []int = make([]int, 10, 50)

或者

v := make([]int, 10, 50)

這樣分配一個有 50 個 int 值的數(shù)組,并且創(chuàng)建了一個長度為 10,容量為 50 的 切片 v,該 切片 指向數(shù)組的前 10 個元素。

問題 7.3 給定 s := make([]byte, 5),len(s) 和 cap(s) 分別是多少?s = s[2:4],len(s) 和 cap(s) 又分別是多少?
問題 7.4 假設(shè) s1 := []byte{'p', 'o', 'e', 'm'}s2 := s1[2:],s2 的值是多少?如果我們執(zhí)行 s2[1] = 't',s1 和 s2 現(xiàn)在的值又分別是多少?

7.2.5 多維 切片

和數(shù)組一樣,切片通常也是一維的,但是也可以由一維組合成高維。通過分片的分片(或者切片的數(shù)組),長度可以任意動態(tài)變化,所以 Go 語言的多維切片可以任意切分。而且,內(nèi)層的切片必須單獨分配(通過 make 函數(shù))。

7.2.6 bytes 包

類型 []byte 的切片十分常見,Go 語言有一個 bytes 包專門用來解決這種類型的操作方法。

bytes 包和字符串包十分類似(參見第 4.7 節(jié))。而且它還包含一個十分有用的類型 Buffer:

import "bytes"

type Buffer struct {
    ...
}

這是一個長度可變的 bytes 的 buffer,提供 Read 和 Write 方法,因為讀寫長度未知的 bytes 最好使用 buffer。

Buffer 可以這樣定義:var buffer bytes.Buffer。

或者使用 new 獲得一個指針:var r *bytes.Buffer = new(bytes.Buffer)

或者通過函數(shù):func NewBuffer(buf []byte) *Buffer,創(chuàng)建一個 Buffer 對象并且用 buf 初始化好;NewBuffer 最好用在從 buf 讀取的時候使用。

通過 buffer 串聯(lián)字符串

類似于 Java 的 StringBuilder 類。

在下面的代碼段中,我們創(chuàng)建一個 buffer,通過 buffer.WriteString(s) 方法將字符串 s 追加到后面,最后再通過 buffer.String() 方法轉(zhuǎn)換為 string:

var buffer bytes.Buffer
for {
    if s, ok := getNextString(); ok { //method getNextString() not shown here
        buffer.WriteString(s)
    } else {
        break
    }
}
fmt.Print(buffer.String(), "\n")

這種實現(xiàn)方式比使用 += 要更節(jié)省內(nèi)存和 CPU,尤其是要串聯(lián)的字符串數(shù)目特別多的時候。

練習 7.5 給定切片 sl,將一個 []byte 數(shù)組追加到 sl 后面。寫一個函數(shù) Append(slice, data []byte) []byte,該函數(shù)在 sl 不能存儲更多數(shù)據(jù)的時候自動擴容。
練習 7.6 把一個緩存 buf 分片成兩個 切片:第一個是前 n 個 bytes,后一個是剩余的,用一行代碼實現(xiàn)。

鏈接