天天看點

Go基礎之--數組和切片

數組

數組的定義:

數組是具有固定長度并擁有零個或者多個相同資料類型元素的序列

定義一個數組的方法:

var 變量名[len] type

例子:

var a[5] int //3個整數的數組

var a[5]string //3個字元串的數組

像上面這種定義方法,我們是指定了數組的長度,但是還有如下定義方法:

var a=[...]int{1,2,3}

如果把數組的長度替換為...,那麼數組的長度由初始化數組的元素個數決定

數組中的每個元素是通過索引來通路,索引是從0開始

例如 數組var a[5]int 擷取第一個元素就是a[0],

擷取數組的長度是通過len(a)

這裡需要知道:數組的長度也是數組類型的一部分,是以要知道[3]int和[4]int是不同的數組類型

預設情況下一個新數組中的元素初始值為元素類型的零值

如一個證書類型的數組,預設值就是0

初始化數組:

有一下幾種方法:

var a = [5] int{1,2,3,4,5}

var a = [5] int{1,2,3}

var a = [...]int{1,2,3,4}

var a = [5]string{1:"go",3:"python"}

關于數組的類型:

值類型

數組的周遊

數組的周遊方法:

var a = [3]int{1, 2, 3}

for i, v := range a {

fmt.Printf("%d %d\n", i, v)

}

當然如果不需要索引也可以:

for _, v := range a {

fmt.Printf("%d\n", v)

二維數組

var a[3][2]

其實二維數組可以通過excel表格了解,就是幾行幾列的問題,像上面的這個例子就是一個3行2列的二維數組。

關于二維數組的周遊,建立一個二維數組并循環指派,然後循環列印内容

var c [3][2]int

for i := 0; i < 3; i++ {

for j := 0; j < 2; j++ {

c[i][j] = rand.Intn(10)

fmt.Printf("%d ", c[i][j])

fmt.Println()

關于數組的比較

如果兩個數組的元素類型相同是可以互相比較的,例如數組a:= [2]int{1,2}和數組b:=[2]int{3,4}

因為同樣都是int類型,是以可以通過==來比較兩個數組,看兩邊的元素是否完全相同,使用!= 比較看兩邊的元素是否不同

通過下面的例子示範更加清晰:

a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{3, 2}
d := [3]int{1, 2}
fmt.Println(a == b, a == c, b == c)
fmt.Println(a == d)      

上面的例子中第一個列印的結果是true,false,false,而當添加第二個列印的時候,就無法編譯過去,因為兩者是不能比較的

切片slice

定義

slice 表示一個擁有相同類型元素的可變長的序列

定義一個slice其實和定義一個數組非常類似

var 變量名[]type

var b = []int

和數組對比slice似乎就是一個沒有長度的數組

slice的初始化

var a[5] int //這是定義一個數組

var b[]int = a[0,2]

var b[]int = a[0:5]

var b[]int = a[:]

var b[]int = a[:3]

var b[] int = []int{1,2,3,4}

同樣周遊切片和數組是一模一樣的

通過把數組和slice對比我們其實可以發現,兩者其實非常類似,當然兩者也确實有着緊密的關系

slice的底層實作就是一個數組,通常我們會叫做slice的底層數組。

slice具有三個屬性:指針,長度和容量,如下圖

Go基礎之--數組和切片

指針指向數組的第一個可以從slice中通路的元素,這個元素不一定是數組的第一個元素

長度是指slice中元素的個數,不能超過slice的容量

容量的大小是從slice的起始元素到底層數組的最後一個元素的個數

通過len和cap可以擷取slice的長度和容量

通過下面例子了解:

var s = [5]int{1, 2, 3, 4, 5}

var b = s[2:3]

var c = s[0:4]

現在問b的長度以及容量,c的長度以及容量

對比上面的定義其實很好明白

s 就好比slice的底層數組

而對于b這個slice來說他是從數組的第三個元素開始切片,切片的時候是左閉右開原則

是以b的長度是1

對于b的容量根據定義我們知道是從數組的第三個元素到數組的最後

是以b的容量是3

這樣我們也可以很容易得到c的長度是3,容量是5

slice建立

内置函數make可以建立一個具有指定元素類型、長度和容量的slice,其中容量參數可以省略,這樣預設slice的長度和容量就相等了

make([]type,len,cap)

make([]type,len)

現在說說關于:

其實make建立了一個無名數組并傳回了它的一個slice;這個數組僅可以通過slice來通路。

第一個:make([]type,len)傳回的slice引用了整個數組。

第二個:make([]type,len,cap)slice隻引用了數組的前len個元素,但是它的容量是數組的長度

通過下圖了解切片的建立過程:

Go基礎之--數組和切片

關于copy

該函數主要是切片(slice)的拷貝,不支援數組

将第二個slice裡的元素拷貝到第一個slice裡。如果加入的兩個數組切片不一樣大,就會按其中較小的那個數組切片的元素個數進行複制。

通過下面例子便于了解:

s1 := []int{1, 2, 3, 7, 8}
s2 := []int{4, 5, 6}
copy(s2, s1)
fmt.Printf("%#v\n", s2)      

這樣列印s2的結果就是:[]int{1, 2, 3}

将代碼更改為:

s1 := []int{1, 2, 3, 7, 8}
s2 := []int{4, 5, 6}
copy(s1, s2)
fmt.Printf("%#v\n", s1)      

這樣列印s1的結果為:[]int{4, 5, 6, 7, 8}

這次拷貝就是把s2中的前三個元素拷貝到s1中的前三個,把s1中的前三個進行了覆寫

關于append

内置的函數append可以把元素追加到slice的後面

通過下面例子了解,把“hello go”每個字元循環添加到一個slice中

var runnes []rune
for _, v := range "hello,go" {
    runnes = append(runnes, v)
}
fmt.Printf("%q\n", runnes)      

例子2直接在一個已經有元素的slice追加

s1 := []int{1, 2, 3}
s1 = append(s1, 4, 5)
fmt.Printf("%#v\n", s1)      

如果想要把另外一個slice也直接append到現在的slice中:

s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2...)
fmt.Printf("%#v\n", s1)      

這裡在s2後面通過...其實就是把s2中的元素給展開然後在append進s1中

其實append函數對于了解slice的工作原理是非常重要的,下面是一個為[]int數組slice定義的一個方法:

func appendInt(x []int, y int) []int {
    var z []int
    zlen := len(x) + 1
    if zlen <= cap(x) {
        //slice仍有增長空間擴充slice内容
        z = x[:zlen]
    } else {
        //slice 已經沒有空間,為他配置設定一個新的底層數組
        //當然實際go底層擴充的時候的政策可能複雜的多,這裡是通過擴充一倍為例子
        zcap := zlen
        if zcap < 2*len(x) {
            zcap = 2 * len(x)
        }
        z = make([]int, zlen, zcap)
        copy(z, x)
    }
    z[len(x)] = y
    return z         

從上面的這個方法可以看出:

每次appendInt的時候都會檢查slice是否有足夠的容量來存儲數組中的新元素,如果slice容量足夠,那麼他會定義一個新的slice,注意這裡仍然引用原始的底層數組,然後将新元素y複制到新的位置,并傳回新的slice,這樣我們傳入的參數切片x和函數傳回值切片z其實用的是相同的底層數組。

如果slice的容量不夠容納增長的元素,appendInt函數必須建立一個擁有足夠容量的新的底層數組來存儲新的元素,然後将元素從切片x複制到這個數組,再将新元素y追加到數組後面。這樣傳回的切片z将和傳入的參數切片z引用不同的底層數組。

關于切片的比較

和數組不同的是,切片是無法比較的,是以不能通過==來比較兩個切片是否擁有相同的元素

slice唯一允許的比較操作是和nill比較,切片的零值是nill

這裡需要注意的是:值為nill的slice的長度和容量都是零,但是這不是決定的,因為存在非nill的slice的長度和容量是零是以想要檢查一個slice是否為還是要使用len(s) == 0 而不是s == nill

下面是整理的練習切片使用的例子

如何修改一個字元串?

package main

import (
    "fmt"
)

func changeString(str1 string) {
    var runnes = []rune(str1)
    runnes[0] = 'h'
    res := string(runnes)
    fmt.Println(res)
}

func main() {
    changeString("Hello,Go")
}      

這裡是把開頭的大寫的h換成了小寫

再看一個例子:

實作字元串的反轉

func reverseStr(str1 string) {
    var runes = []rune(str1)
    var res string
    for i := len(runes) - 1; i >= 0; i-- {
        res += string(runes[i])
    }
    fmt.Println(res)
}      

上面這個方法就可以實作對字元串的反轉,當然方法不止一種,下面也是一種方法

func reverseStr2(str1 string) {
    var runes = []rune(str1)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    res := string(runes)
    fmt.Println(res)
}      

上面的方法中我一直在用到rune,這個東西是什麼東西呢?接着看

GO當中的:string rune,byte

在Go當中的字元換string 底層是用byte數組存的,并且是不可改變的

當我們通過for key, value := range str這種方式循環一個字元串的時候,其實傳回的每個value類型就是rune

而我們知道在go中雙引号引起來的是字元串string,在go中表示字元串有兩種方式:

一種是byte,代表utf-8字元串的單個位元組的值;另外一個是rune,代表單個unicode字元串

關于rune官網中一段解釋:

rune is an alias for int32 and is equivalent to int32 in all ways. It is

used, by convention, to distinguish character values from integer values.

我們通過下面的代碼例子來了解一下:

var a = "我愛你go"

fmt.Println(len(a))

上面已經說了,字元串的底層是byte位元組數組,是以我們通過len來計算長度的時候,其實就是擷取的該數組的長度,而一個中文字元是占3個位元組,是以上面的結果是11

可能很多人第一眼看的時候,尤其初學者可能會覺得長度應該是5,其實,如果想要轉換成4隻需要通過蝦米那方式就可以:

fmt.Println(len([]rune(a)))

時間和日期類型

目前時間:now:= time.Now()

time.Now().Day()

time.Now().Minute()

time.Now().Month()

time.Now().Year()

time.Duration用來表示納秒

一些常用的時間常量

const (

Nanosecond Duration = 1

Microsecond =1000 * Nanosecond

Millisecond =1000 * Microsecond

Second =1000 * Millisecond

Minute =60 * Second

Hour =60 * Minute

)

注意:如果想要格式化時間的時候,要特别特别注意,隻能通過如下方式格式化:

fmt.Println(time.Now().Format("2006-01-02 15:04:05"))

Format裡面的時間是固定的,因為是go第一個程式的誕生時間,也不知道go的開發者怎麼想的,估計是想讓所有學習go的人記住這個偉大的時刻吧

切片處理補充

關于切片删除

代碼例子:

package main

import "fmt"

func main() {
    index := 2
    var s = []int{10,15,8,20}
    s = append(s[:index],s[index+1:]...)
    fmt.Println(s)
}      

所有的努力都值得期許,每一份夢想都應該灌溉!