Go學習(4):陣列和切片
一、陣列(Array)
1.1 什麼是陣列
Go 語言提供了陣列型別的資料結構。
陣列是具有相同唯一型別的一組已編號且長度固定的資料項序列,這種型別可以是任意的原始型別例如整形、字串或者自定義型別。
陣列元素可以通過索引(位置)來讀取(或者修改),索引從0開始,第一個元素索引為 0,第二個索引為 1,以此類推。陣列的下標取值範圍是從0開始,到長度減1。
陣列一旦定義後,大小不能更改。這些性質和java中的陣列類似.
1.2 陣列的語法
宣告和初始化陣列
需要指明陣列的大小和儲存的資料型別。
var variable_name [SIZE] variable_type
示例程式碼:
var balance [10] float32
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
初始化陣列中 {} 中的元素個數不能大於 [] 中的數字。
如果忽略 [] 中的數字不設定陣列大小,Go 語言會根據元素的個數來設定陣列的大小:
var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance[4] = 50.0
陣列的其他建立方式:
var a [4] float32 // 等價於:var arr2 = [4]float32{}
fmt.Println (a) // [0 0 0 0]
var b = [5] string{"ruby", "王二狗", "rose"}
fmt.Println(b) // [ruby 王二狗 rose ]
var c = [5] int{'A', 'B', 'C', 'D', 'E'} // byte
fmt.Println(c) // [65 66 67 68 69]
d := [...] int{1,2,3,4,5}// 根據元素的個數,設定陣列的大小
fmt.Println(d)//[1 2 3 4 5]
e := [5] int{4: 100} // [0 0 0 0 100]
fmt.Println(e)
f := [...] int{0: 1, 4: 1, 9: 1} // [1 0 0 0 1 0 0 0 0 1]
fmt.Println(f)
備註 :
Println :可以打印出字串,和變數
Printf : 只可以打印出格式化的字串,可以輸出字串型別的變數,不可以輸出整形變數和整形
當需要格式化輸出資訊時一般選擇 Printf,其他時候用 Println 就可以了
訪問陣列元素
float32 salary = balance[9]
示例程式碼:
package main
import "fmt"
func main() {
var n [10]int /* n 是一個長度為 10 的陣列 */
var i,j int
/* 為陣列 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 設定元素為 i + 100 */
}
/* 輸出每個陣列元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}
執行結果:
Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109
陣列的長度
通過將陣列作為引數傳遞給len函式,可以獲得陣列的長度。
示例程式碼:
package main
import "fmt"
func main() {
a := [...]float64{67.7, 89.8, 21, 78}
fmt.Println("length of a is",len(a))
}
執行結果:
length of a is 4
您甚至可以忽略宣告中陣列的長度並將其替換為…讓編譯器為你找到長度。這是在下面的程式中完成的。
示例程式碼:
package main
import (
"fmt"
)
func main() {
a := [...]int{12, 78, 50} // ... makes the compiler determine the length
fmt.Println(a)
}
遍歷陣列:
package main
import "fmt"
func main() {
a := [...]float64{67.7, 89.8, 21, 78}
for i := 0; i < len(a); i++ { //looping from 0 to the length of the array
fmt.Printf("%d th element of a is %.2f\n", i, a[i])
}
}
使用range遍歷陣列:
package main
import "fmt"
func main() {
a := [...]float64{67.7, 89.8, 21, 78}
sum := float64(0)
for i, v := range a {//range returns both the index and value
fmt.Printf("%d the element of a is %.2f\n", i, v)
sum += v
}
fmt.Println("\nsum of all elements of a",sum)
}
如果您只需要值並希望忽略索引,那麼可以通過使用_ blank識別符號替換索引來實現這一點。
for _, v := range a { //ignores index
}
1.3 多維陣列
Go 語言支援多維陣列,以下為常用的多維陣列宣告語法方式:
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
var threedim [5][10][4]int
二維陣列
多維陣列可通過大括號來初始值。以下例項為一個 3 行 4 列的二維陣列:
a = [3][4]int{
{0, 1, 2, 3} , /* 第一行索引為 0 */
{4, 5, 6, 7} , /* 第二行索引為 1 */
{8, 9, 10, 11} /* 第三行索引為 2 */
}
int val = a[2][3]
以上例項訪問了二維陣列 val 第三行的第四個元素。
遍歷二維陣列:
package main
import "fmt"
func main() {
/* 陣列 - 5 行 2 列*/
var a = [5][2]int{ {0,0}, {1,2}, {2,4}, {3,6},{4,8}}
var i, j int
/* 輸出陣列元素 */
for i = 0; i < 5; i++ {
for j = 0; j < 2; j++ {
fmt.Printf("a[%d][%d] = %d\n", i,j, a[i][j] )
}
}
}
1.4 陣列是值型別
陣列是值型別
Go中的陣列是值型別,而不是引用型別。這意味著當它們被分配給一個新變數時,將把原始陣列的副本分配給新變數。如果對新變數進行了更改,則不會在原始陣列中反映。
package main
import "fmt"
func main() {
a := [...]string{"USA", "China", "India", "Germany", "France"}
b := a // a copy of a is assigned to b
b[0] = "Singapore"
fmt.Println("a is ", a)
fmt.Println("b is ", b)
}
執行結果:
a is [USA China India Germany France]
b is [Singapore China India Germany France]
類似地,當將陣列傳遞給函式作為引數時,它們將通過值傳遞,而原始陣列將保持不變。
向函式傳遞陣列
第一種
void myFunction(param [10]int)
{
.
.
.
}
第二種
void myFunction(param []int)
{
.
.
.
}
func getAverage(arr []int, int size) float32
{
var i int
var avg, sum float32
for i = 0; i < size; ++i {
sum += arr[i]
}
avg = sum / size
return avg;
}
package main
import "fmt"
func changeLocal(num [5]int) {
num[0] = 55
fmt.Println("inside function ", num)
}
func main() {
num := [...]int{5, 6, 7, 8, 8}
fmt.Println("before passing to function ", num)
changeLocal(num) //num is passed by value
fmt.Println("after passing to function ", num)
}
執行結果:
before passing to function [5 6 7 8 8]
inside function [55 6 7 8 8]
after passing to function [5 6 7 8 8]
陣列的大小是型別的一部分。因此[5]int和[25]int是不同的型別。因此,陣列不能被調整大小。不要擔心這個限制,因為切片的存在是為了解決這個問題。
package main
func main() {
a := [3]int{5, 78, 8}
var b [5]int
b = a //not possible since [3]int and [5]int are distinct types
}
二、切片(Slice)
2.1 什麼是切片
Go 語言切片是對陣列的抽象。
Go 陣列的長度不可改變,在特定場景中這樣的集合就不太適用,Go中提供了一種靈活,功能強悍的內建型別切片(“動態陣列”),與陣列相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大
切片是一種方便、靈活且強大的包裝器。切片本身沒有任何資料。它們只是對現有陣列的引用。
切片與陣列相比,不需要設定長度,在[]中不用設定值,相對來說比較自由
從概念上面來說slice像一個結構體,這個結構體包含了三個元素:
- 指標,指向陣列中slice指定的開始位置
- 長度,即slice的長度
- 最大長度,也就是slice開始位置到陣列的最後位置的長度
2.2 切片的語法
定義切片
var identifier []type
切片不需要說明長度。
或使用make()函式來建立切片:
var slice1 []type = make([]type, len)
也可以簡寫為
slice1 := make([]type, len)
make([]T, length, capacity)
初始化
s[0] = 1
s[1] = 2
s[2] = 3
s :=[] int {1,2,3 }
s := arr[startIndex:endIndex]
將arr中從下標startIndex到endIndex-1 下的元素建立為一個新的切片(前閉後開),長度為endIndex-startIndex
s := arr[startIndex:]
預設endIndex時將表示一直到arr的最後一個元素
s := arr[:endIndex]
預設startIndex時將表示從arr的第一個元素開始
package main
import (
"fmt"
)
func main() {
a := [5]int{76, 77, 78, 79, 80}
var b []int = a[1:4] //creates a slice from a[1] to a[3]
fmt.Println(b)
}
2.3 修改切片
slice沒有自己的任何資料。它只是底層陣列的一個表示。對slice所做的任何修改都將反映在底層陣列中。
示例程式碼:
package main
import (
"fmt"
)
func main() {
darr := [...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}
dslice := darr[2:5]
fmt.Println("array before",darr)
for i := range dslice {
dslice[i]++
}
fmt.Println("array after",darr)
}
執行結果:
array before [57 89 90 82 100 78 67 69 59]
array after [57 89 91 83 101 78 67 69 59]
當多個片共享相同的底層陣列時,每個元素所做的更改將在陣列中反映出來。
示例程式碼:
package main
import (
"fmt"
)
func main() {
numa := [3]int{78, 79 ,80}
nums1 := numa[:] //creates a slice which contains all elements of the array
nums2 := numa[:]
fmt.Println("array before change 1",numa)
nums1[0] = 100
fmt.Println("array after modification to slice nums1", numa)
nums2[1] = 101
fmt.Println("array after modification to slice nums2", numa)
}
執行結果:
array before change 1 [78 79 80]
array after modification to slice nums1 [100 79 80]
array after modification to slice nums2 [100 101 80]
2.4 len() 和 cap() 函式
切片的長度是切片中元素的數量。切片的容量是從建立切片的索引開始的底層陣列中元素的數量。
切片是可索引的,並且可以由 len() 方法獲取長度
切片提供了計算容量的方法 cap() 可以測量切片最長可以達到多少
package main
import "fmt"
func main() {
var numbers = make([]int,3,5)
printSlice(numbers)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
結果
len=3 cap=5 slice=[0 0 0]
空切片
一個切片在未初始化之前預設為 nil,長度為 0
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
if(numbers == nil){
fmt.Printf("切片是空的")
}
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
結果
len=0 cap=0 slice=[]
切片是空的
package main
import "fmt"
func main() {
/* 建立切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 列印原始切片 */
fmt.Println("numbers ==", numbers)
/* 列印子切片從索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 預設下限為 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 預設上限為 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 列印子切片從索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 列印子切片從索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
結果
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]
2.5 append() 和 copy() 函式
append 向slice裡面追加一個或者多個元素,然後返回一個和slice一樣型別的slice
copy 函式copy從源slice的src中複製元素到目標dst,並且返回複製的元素的個數
append函式會改變slice所引用的陣列的內容,從而影響到引用同一陣列的其它slice。 但當slice中沒有剩
餘空間(即(cap-len) == 0)時,此時將動態分配新的陣列空間。返回的slice陣列指標將指向這個空間,而原
陣列的內容將保持