天天看點

Go的基本資料類型

由于本人一直使用 PHP 語言進行開發,在學習 Go 的過程中發現有些知識點經常混淆,遂将 Go 與 PHP 做了比較(僅限于二者的基本資料類型)。

這是 Go 學習筆記的第一篇,主要是 Go 基本資料類型的學習總結。

Go PHP
基本資料類型

布爾型

數字類型:

  • 整型(int、unit)
  • 浮點型(float32、float64)
  • 其它:byte(類似unit8)、rune(類似int32)等

字元串類型

派生類型:

  • 指針類型(Pointer)
  • 數組類型(Array)
  • 結構類型(Struct)
  • 管道類型(Channel)
  • 函數類型(Function)
  • 切片類型(Slice)
  • 接口類型(Interface)
  • Map類型(Map)
4 種标量類型:
  • boolean(布爾型)
  • integer(整型)
  • float(浮點型,也稱作 double)
  • string(字元串)
3 種複合類型:
  • array(數組)
  • object(對象)
  • callable(可調用)
2 種特殊類型:
  • resource(資源)
  • NULL(無類型)
字元串 位元組編碼
  • 字元串的位元組預設使用 UTF-8 編碼
  • 支援 Unicode 字元
  • 隻支援 256 的字元集,需要在代碼中顯式指定字元編碼 header("Content-Type: text/html;charset=utf-8"); 或修改php.ini中有關預設字元集的設定 default_charset = "utf-8" 
  • 不支援 Unicode 字元
字元串定義
  • 雙引号(""):雙引号中的轉義字元會被替換
  • 反引号(``):反引号中原生字元串中的轉義字元(如\n)會被原樣輸出
  • 單引号(''):單引号括起來的單個字元,需要格式化輸出,%c,否則輸出字元碼數
  • 雙引号(""):雙引号中的轉義字元會被替換,可以解析變量,即将變量包含在雙引号中
  • 單引号(''):與Go的反引号類似,轉義字元不會被轉義,原樣輸出
字元串長度

 len() :擷取的是每個字元的 UTF-8 編碼的長度和,而不是直接的字元數量。

如 len("hello,世界") 的結果是 12 不是 8

 strlen() :與 Go 的  len() 類似,得到的是字元串所占的位元組數

 utf8.RuneCountInString() :擷取字元串的真正長度,上面的 utf8.RuneCountInString("hello,世界") 的結果是 8。

注:需import "unicode/utf8"

擷取字元串的真正長度可以使用下面兩個函數:
  •  mb_strlen() :需安裝 mbstring 擴充
  •  iconv_strlen() :傳回字元串的字元數統計,是整型
字元串拼接 運算符 + :
str := "hello, " +
"world"      
  • 字元串之間用加号“+”拼接。如果換行,+ 要放到目前行的末尾,不能放到下一行的開頭
  • 使用這種拼接方式,裡面的字元串是不可變的,每次運算都會産生一個新的、臨時的字元串,給 GC 帶來額外的負擔,是以性能比較差
運算符 . :
$str = "hello, " . "world";
// 也可以寫成:
$str = "hello, " 
       . "world";      
  • 字元串之間用句點 “.”拼接,換行對句點的位置沒有要求
fmt.Sprintf():
fmt.Sprintf("%d:%s", 2018, "年")      
  • 内部使用 []byte 實作,不會産生臨時的字元串
  • 内部邏輯複雜,還用到了interface,性能一般
strings.Join()
strings.Join([]string{"hello", "world"}, ", ")      
  • Join會先根據字元串數組的内容,計算出一個拼接之後的長度,然後申請對應大小的記憶體,一個一個字元串填入
  • 已有一個數組的情況下,這種拼接方式的效率很高,但如果沒有,去構造這個資料的代價也不小。
bytes.Buffer——優先推薦
var buffer bytes.Buffer
buffer.WriteString("hello")
buffer.WriteString(", ")
buffer.WriteString("world")
 
fmt.Print(buffer.String())      
  • 使用這種拼接方式,可以把字元串當成可變字元使用,對記憶體的增長也有優化
  • 如果能預估字元串的長度,還可以用 buffer.Grow() 接口來設定 capacity。
strings.Builder——推薦
var b1 strings.Builder
b1.WriteString("ABC")
b1.WriteString("DEF")
 
fmt.Print(b1.String())      
  • 内部通過 slice 來儲存和管理内容
  • 提供了 Grow() 來支援預定義容量,這樣可以避免擴容而建立新的 slice
  • 非線程安全,性能上和 bytes.Buffer 相差無幾
數組 聲明數組
  • 數組元素必須類型唯一,要麼都是字元串,要麼都是數字類型。如果想讓數組元素類型為任意類型,可以使用空接口interface{}作為類型,當使用值時必須先做一個類型判斷。
  • 聲明時需要确定長度,如果采用不定長數組的方式聲明,在初始化時會自動确定好數組的長度。

① var arr [2]int //var 數組名稱 [數組長度]數組元素類型

② var arr = [2]int{1,2} //var 數組名稱 = [數組長度]數組元素類型{元素1,元素2,...}

③ var arr = [5]string{3: "ab", 4: "cd"} //指定索引位置

④ var arr = [...]int{1,2} //var 數組名稱 = [...]數組元素類型{元素1,元素2,...},不定長數組聲明方式

⑤ new():建立的是數組引針,eg.  var arr1 = new([5]int) 

  • 聲明時不需要确定長度,且數組元素可以多類型混合
$arr  = ['a', 'b', 'c', 123];      
如果是 Go,會報錯:
# command-line-arguments
./arr.go:6:34: cannot use 123 (type int) as type string in array or slice literal      

值傳遞和

引用傳遞

var arr1 = new([5]int)
arr := arr1
arr1[2] = 100
fmt.Println(arr1[2], arr[2])
 
var arr2 [5]int
newarr := arr2
arr2[2] = 100
fmt.Println(arr2[2], newarr[2])
程式輸出:
100 100
100 0      

new([5]int)建立的是數組指針,arr和arr1指向同一位址,故而修改arr1時arr同樣也生效;

而newarr是由arr2值傳遞(拷貝),故而修改任何一個都不會改變另一個的值。

達到與左側相同的效果,PHP 代碼如下:
$arr1 = [5, 10, 0, 20, 25];
$arr = &$arr1; //引用傳遞
$newArr = $arr1; //值傳遞
$arr1[2] = 100;
echo $arr1[2], $arr[2];
echo $arr1[2], $newArr[2];
 
程式輸出:
100 100
100 0      
PHP中的引用傳遞使用 & 符号。
切片 定義
  • 切片是引用(對底層數組一個連續片段的引用),不需要占用額外的記憶體,比數組效率高
  • 一個切片未初始化前預設為nil,長度為0
  • 切片的長度可變,可以把切片看成一個長度可變的數組
PHP中沒有切片這個概念,但是在數組函數中有個 array_slice() 函數,可以根據 offset 和 length 傳回指定的數組中的一段序列。與 Go 的切片相似。
$input = array("a", "b", "c", "d", "e");
$output = array_slice($input, 0, 3);   // returns "a", "b", and "c"      
聲明

① 切分數組

var arr = [5]int{1,2,3,4,5} //數組

var s []type = arr[start:end:max] //切片(包含數組start到end-1之間的元素),end-start表示長度,max-start表示容量

② 初始化

var s = []int{2, 3, 5, 7, 11} //建立了一個長度為 5 的數組并且建立了一個相關切片。

③ 切片重組

var s []type = make([]type, len, cap) //len 是數組的長度也是切片的初始長度,cap是容量,可選參數

簡寫為slice := make([]type, len)

字典
  • 一種元素對的無序集合,一組稱為元素value,另一組為唯一鍵索引key
  • 未初始化 map 的值為 nil。map 是引用類型
  • map 可以動态增長,聲明時不需要定義長度
PHP的關聯數組類似于 Go 的 Map。

① var mapName map[keyType]valueType

② var mapName map[keyType]valueType{k1:v1, k2:v2, ...}

③ var mapName = make(map[keyType]valueType, cap) //cap是可選參數,表示容量

其實 Go 的基本資料類型的知識點以及它和 PHP 的差別絕不止上面列出的這些,對比新舊兩種語言的差别意義也不是很大,但對我來說,通過梳理可以達到溫故知新的目的。 

go