1. 程式人生 > >Go語言學習(四)經常使用類型介紹

Go語言學習(四)經常使用類型介紹

初始化 過程 lines 第一個 方法 管理 每一個 lds run

1.布爾類型

var v1 bool
v1 = true;
v2 := (1==2) // v2也會被推導為bool類型

2.整型

類 型  長度(字節)     值 範 圍
int8    1  ?            128 ~ 127
uint8(即byte)1       0 ~ 255
int16   2  ?            32 768 ~ 32 767
uint16  2               0 ~ 65 535
int32   4  ?            2 147 483 648 ~ 2 147 483 647
uint32  4               0 ~ 4 294 967 295
int64   8  ?    9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
uint64  8       0 ~ 18 446 744 073 709 551 615
int     平臺相關        平臺相關
uint    平臺相關        平臺相關
uintptr 同指針         在32位平臺下為4字節,64位平臺下為8字節
var value2 int32
value1 := 64 // value1將會被自己主動推導為int類型
value2 =value1  //編譯失敗:cannot use value1 (type int) as type int32 in assignment。

//使用強制類型轉換可解決 value2 = int32(value1)

3.數值運算

常規運算:+、-、*、/和%
比較運算: > 、 < 、 == 、 >= 、 <= 和 !=
須要註意:兩個不同類型的整型數不能直接比較,比方 int8 類型的數和 int類型的數不能直接比較,但各種
類型的整型變量都能夠直接與字面常量(literal)進行比較,比方:
var i int32
var j int64
i, j = 1, 2
if i == j { // 編譯錯誤
fmt.Println("i and j are equal.")
}
if i == 1 || j == 2
{ // 編譯通過 fmt.Println("i and j are equal.") }

4.位運算

x << y  左移 
x >> y  右移 
x ^ y   異或  
x & y   與  
x | y   或 
^x      取反  

5.浮點類型

Go語言定義了兩個類型 float32 和 float64 ,當中 float32 等價於C語言的 float 類型,
float64 等價於C語言的 double 類型。

var fvalue1 float32
fvalue1 = 12
fvalue2 := 12.0 // 假設不加小數點,fvalue2會被推導為整型而不是浮點型

對於以上樣例中類型被自己主動推導的 fvalue2 。須要註意的是其類型將被自己主動設為 float64 ,
而無論賦給它的數字是否是用32位長度表示的。因此,對於以上的樣例。以下的賦值將導致編譯
錯誤:
fvalue1 = fvalue2
而必須使用這種強制類型轉換:
fvalue1 = float32(fvalue2)

5.1浮點數比較

由於浮點數不是一種精確的表達方式,所以像整型那樣直接用 == 來推斷兩個浮點數是否相等
是不可行的。這可能會導致不穩定的結果。
以下是一種推薦的替代方案:
import "math"
// p為用戶自己定義的比較精度,比方0.00001
func IsEqual(f1, f2, p float64) bool {
    return math.Fdim(f1, f2) < p
}

6.復數

復數實際上由兩個實數(在計算機中用浮點數表示)構成,一個表示實部(real),一個表示
虛部(imag)。假設了解了數學上的復數是怎麽回事,那麽Go語言的復數就很easy理解了。

6.1復數表示

var value1 complex64 // 由2個float32構成的復數類型
value1 = 3.2 + 12i
value2 := 3.2 + 12i // value2是complex128類型
value3 := complex(3.2, 12) // value3結果同 value2

6.2 實部與虛部

對於一個復數 z = complex(x, y) ,就能夠通過Go語言內置函數 real(z) 獲得該復數的實
部,也就是 x ,通過 imag(z) 獲得該復數的虛部,也就是 y 。

7.字符串

Go語言中字符串的聲明和初始化很簡單。舉比例如以下:
var str string  // 聲明一個字符串變量
str = "Hello world" // 字符串賦值
ch := str[0] // 取字符串的第一個字符
fmt.Printf("The length of \"%s\" is %d \n", str, len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
輸出結果為:
The length of "Hello world" is 11
The first character of "Hello world" is H.

字符串的內容能夠用相似於數組下標的方式獲取,但與數組不同,字符串的內容不能在初始
化後被改動,比方以下的樣例:
str := "Hello world" // 字符串也支持聲明時進行初始化的做法
str[0] = ‘X‘  // 編譯錯誤
編譯器會報相似例如以下的錯誤:
cannot assign to str[0]
假設想改動字符串的內容,能夠通過例如以下的方式改動:
s := "hello"
c := []rune(s) //將字符串s轉成rune數組
c[0] = ‘x‘ //改動rune數組中的第一個元素為x
s2 := string(c) //根據rune數組創建一個新的字符串
fmt.Println("s2=",s2)
執行結果:
    s2= xello

7.1字符串操作

x + y   字符串連接  "Hello" + "123" // 結果為Hello123
len(s)  字符串長度  len("Hello") // 結果為5
s[i]    取字符  "Hello" [1] // 結果為‘e‘

7.2多行字符串

使用多行字符串須要小心,比如:
s := "Hello "
    +"world"

編譯時會被自己主動轉換成:
s := "Hello ";
    +"world";
此時會編譯失敗,報錯:invalid operation: + untyped string  

正確的多行寫法是這種:
s := "Hello " +
    "world"
Go 就不會在錯誤的地方插入分號。

還有一種寫法是使用反引號 ` 作為原始字符串符號: s := `Hello world`

7.3字符串遍歷

Go語言支持兩種方式遍歷字符串。

一種是以字節數組的方式遍歷:

str := "abc中國"
n := len(str)
for i := 0; i < n; i++ {
    ch := str[i] // 根據下標取字符串中的字符,類型為byte
    fmt.Printf("i=%v\n", ch)
}
輸出結果:
i=97    //a
i=98    //b
i=99    //c
i=228   //中字的第一字節,每一個中文字符在UTF-8中占3個字節
i=184   //中字的第二個字節
i=173   //中字的第三個字節
i=229   //國字的第一個字節
i=155   //國字的第二個字節
i=189   //國字的第三個字節

還有一種是以Unicode字符遍歷,以Unicode字符方式遍歷時,每一個字符的類型是 rune 
(早期的Go語言用 int 類型表示Unicode字符),而不是byte.
str := "abc中國"
for i, ch := range str{  //range關鍵字在數組中有介紹
    fmt.Println(i,ch)  //ch的類型為rune
}
//輸出結果:
0 97
1 98
2 99
3 20013   
6 22269

8.字符類型

在Go語言中支持兩個字符類型,一個是 byte (實際上是 uint8 的別名),代表UTF-8字符串的單個字節的值;
還有一個是 rune 。代表單個Unicode字符。出於簡化語言的考慮,Go語言的多數API都假設字符串為UTF-8編碼。
雖然Unicode字符在標準庫中有支持。但實際上較少使用。

9.數組

以下為一些常規的數組聲明方法:
[32]byte // 長度為32的數組,每一個元素為一個字節
[2*N] struct { x, y int32 } // 復雜類型數組
[1000]*float64  // 指針數組
[3][5]int  // 二維數組
[2][2][2]float64  // 等同於[2]([2]([2]float64))

在Go語言中。數組長度在定義後就不可更改,在聲明時長度能夠為一個常量或
者一個常量表達式(常量表達式是指在編譯期就可以計算結果的表達式)。

9.1元素的訪問

能夠使用數組下標來訪問數組中的元素。與C語言同樣,數組下標從0到len(array)-1,
以下的演示樣例遍歷整型數組並逐個打印元素內容:
//通過for循環遍歷
for i := 0; i < len(array); i++ {
    fmt.Println("Element", i, "of array is", array[i])
}
//Go語言還提供了一個關鍵字range,用於便捷地遍歷容器中的元素。
//當然,數組也是range的支持範圍。上面的遍歷過程能夠簡化為例如以下的寫法:
for k, v := range array {  //k相當於索引,v相當於值
    fmt.Println("Array element[", k, "]=", v)  
}

9.2值操作

須要特別註意的是。在Go語言中數組是一個值類型(value type)。

全部的值類型變量在賦值 和作為參數傳遞時都將產生一次復制動作。

假設將數組作為函數的參數類型,則在函數調用時該 參數將發生數據復制。

因此,在函數體中無法改動傳入的數組的內容,由於函數內操作的僅僅是所 傳入數組的一個副本。以下用樣例來說明這一特點:

func main(){
    array := [5]int{1,2,3,4,5}
    modify(array)
    fmt.Println("In main(),array values:",array)
}

func modify(array [5] int){
    array[0] = 10
    fmt.Println("In modify(),array values:",array)
}
//輸出結果:
In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]

從執行結果能夠看出,函數 modify() 內操作的那個數組跟 main() 中傳入的數組是兩個不同的實
例。那麽。怎樣才幹在函數內操作外部的數據結構呢?這個就要用到後接下來要講的數組切片.

10.數組切片

在前一節裏我們已經提過數組的特點:數組的長度在定義之後無法再次改動;數組是值類型。
每次傳遞都將產生一份副本。顯然這種數據結構無法全然滿足開發人員的真實需求。

Go語言提供了數組切片(slice)這個很酷的功能來彌補數組的不足,能夠隨時動態擴充存放空 間,而且能夠被任意傳遞而不會導致所管理的元素被反復復制。有點相似於java中的集合. 數組切片的數據結構能夠抽象為以下3個變量: 一個指向原生數組的指針; 數組切片中的元素個數; 數組切片已分配的存儲空間。

10.1創建數組切片

(1)基於已存在數組創建,數組切片能夠僅僅使用數組的一部分元素或者整個數組來創建。
甚至能夠創建一個比所基於的數組還要大的數組切片。

var myArray [10]int = [10]int{1,2,3,4,5,6,7,8,9,10}
// 基於數組前5個元素創建一個數組切片
var mySlice []int = myArray[:5]

fmt.Println("Elements of myArray:")
for _, v := range myArray{
    fmt.Print(v," ")
}

fmt.Println("\nElements of mySlice:")
for _, v:= range mySlice{
    fmt.Print(v," ")
}
//輸出結果:
Elements of myArray:
1 2 3 4 5 6 7 8 9 10
Elements of mySlice:
1 2 3 4 5

Go語言支持用 myArray[first:last] 這種方式來基於數組生成一
個數組切片,而且這個使用方法還很靈活,比方以下幾種都是合法的。
a.基於 myArray 的全部元素創建數組切片:
mySlice = myArray[:]

b.基於 myArray 的前5個元素創建數組切片:
mySlice = myArray[:5]

c.基於從第5個元素開始的全部元素創建數組切片:
mySlice = myArray[5:]

(2)通過make()函數直接創建數組切片
創建一個初始元素個數為5的數組切片,元素初始值為0:
mySlice1 := make([]int,5)

創建一個初始元素個數為5的數組切片,元素初始值為0,並預留10個元素的存儲空間:
mySlice2 := make([]int,5,10) 

通過cap()函數能夠獲取切片的分配空間,len()函數能夠知道當前已存儲的元素個數.
fmt.Println("len(mySlice2):", len(mySlice2))
fmt.Println("cap(mySlice2):", cap(mySlice2))
//輸出結果:
len(mySlice2): 5
cap(mySlice2): 10

當然,其實還會有一個匿名數組被創建出來。僅僅是不須要我們來擔心而已。

(3)直接在聲明的時候並初始化
mySlice3 := []int{8,9,10}

(4)基於數組切片創建數組切片
oldSlice := []int{1,2,3,4,5}
newSlice := oldSlice[:3] //基於oldSlice的前3個元素創建

10.2數組切片的元素遍歷

操作數組的全部方法都適合數組切片,通過上面的代碼也能夠看得出來共同擁有2種方式:
for循環遍歷,或者for循環結合range來操作

10.3數組切片元素的動態增減

與數組相比,數組切片多了一個存儲能力的概念,即元素個數和分配的空間能夠是兩個不同的值,
合理地設置存儲能力的值,能夠大幅減少數組切片內部又一次分配內存和搬送內存塊的頻率,
從而大大提高程序性能。

通過append()函數能夠在切片後面追加元素,比如: mySlice = append(mySlice, 8, 9, 10) append()函數的第二個參數是個不定的參數,有點相似java中的可變參數,甚至能夠追加1個數組切片 mySlice2 := []int{8,9,10} mySlice = append(mySlice,mySlice2...) //註意:這3個...點不能省略,否則編譯失敗

10.4內容復制

通過copy()函數,能夠將一個數組切片拷貝到還有一個數組切片中,
假設2個的數組切片不一樣大,則會按當中較小的那個數組切片的元素個數進行復制.
slice1 := []int{1,2,3,4,5}
slice2 := []int{5,4,3}
copy(slice2,slice1) //僅僅會復制slice1的前3個元素到slice2中,slice2中的值被自己主動替換
copy(slice1,slice2) //僅僅會復制slice2的3個元素到slice1的前3個位置,該3個位置的值自己主動替換,其它的不變

11.map

在C++/Java中。map一般都以庫(導包)的方式提供,在C++/Java中, 
而在Go中,使用map 不須要引入不論什麽庫。而且用起來也更加方便。看以下的樣例:
type PersonInfo struct{
    ID string
    Name string
    Address string
}

func main(){
    var personDB map[string] PersonInfo //聲明一個key=string,value=PersonInfo的map
    personDB = make(map[string] PersonInfo) //通過make初始化

    //往這個map裏插入幾條數據
    personDB["0"] = PersonInfo{"12345","Tom","Room 203,..."}
    personDB["1"] = PersonInfo{"1","Jack","Room 101,..."}
    //....
    //從這個map查找鍵為12345的信息
    person, ok := personDB["0"] //假設查找到,則ok=true,否則=false
    if ok{
        fmt.Println("Found person",person.Name,"with ID",person.ID)
    }else{
        fmt.Println("Did not find person")
    }
}
//執行結果:
Found person Tom with ID 12345

11.1map的聲明

上面的樣例假設看不懂的話沒關系,以下就介紹下map的使用
map的聲明基本上沒有多余的元素,形如:
var 變量名 map[key的類型] value的類型 

11.2map的創建

(1)通過make()函數創建
var myMap map[int] string
myMap = make(map[int]string) //也能夠簡寫成一條語句: myMap := make(map[int]string)
myMap[0] = "1" //賦值
myMap[1] = "2"
result,_ := myMap[0] //這裏用到了匿名變量
fmt.Println("myMap[0]=",result)
fmt.Println("myMap[1]=",myMap[1])
//執行結果:
myMap[0]= 1
myMap[1]= 2

通過make創建的時候,還能夠指定其初始容量
myMap2 := make(map[int]string,100)

(2)方式2,通過{}賦值的方式創建
myMap3 := map[int]string{
    0:"張三",
    1:"李四",
}
fmt.Println("myMap3[0]=",myMap3[0])
fmt.Println("myMap3[1]=",myMap3[1])
//執行結果:
myMap3[0]= 張三
myMap3[1]= 李四

11.3元素的刪除

通過delete()函數完畢,形如:
delete(map變量名,map的key)
假設要刪除的元素沒有相應的key,則什麽都不發生,可是假設傳入的map變量的值是 nil 。
該調用將導致程序拋出異常(panic)。

11.4元素的查找

map通過key查找,能夠返回2個變量,第一個為相應key的值,第二個為是否查找成功的bool,形如:
value,ok := map[key]
if ok{
    //找到了,處理value
}else{
    //未找到
}
當然,假設你很確定該key一定能夠找到相應的值的話,那就能夠直接使用一個變量來接收,比如:
value := map[key]
或者,第二個參數用匿名參數來接收,比如:
value,_ := map[key]

11.5元素的遍歷

(1)使用for循環遍歷    
myMap := make(map[int]int)
myMap[0] = 100
myMap[1] = 101
myMap[2] = 102
for i:=0;i<len(myMap);i++ {
    fmt.Println("key=",i," value=",myMap[i])
}
執行結果:
key= 0  value= 100
key= 1  value= 101
key= 2  value= 102

(2)使用for循環結合range關鍵字遍歷
myMap := map[int]int{
    0 : 10,
    1 : 20,
    2 : 30,
}
for k,v := range myMap{
    fmt.Println("key=",k," value=",v)
}
執行結果:
key= 0  value= 10
key= 1  value= 20
key= 2  value= 30

Go語言學習(四)經常使用類型介紹